#include "basechart_axis.h"

#include "basechart_graph.h"
#include "basechart_plot.h"
#include "basechart_plotitem.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTicker
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTicker
  \brief The base class tick generator used by BaseChartAxis to create tick
  positions and tick labels

  Each BaseChartAxis has an internal BaseChartAxisTicker (or a subclass) in
  order to generate tick positions and tick labels for the current axis range.
  The ticker of an axis can be set via \ref BaseChartAxis::setTicker. Since that
  method takes a <tt>QSharedPointer<BaseChartAxisTicker></tt>, multiple axes can
  share the same ticker instance.

  This base class generates normal tick coordinates and numeric labels for
  linear axes. It picks a reasonable tick step (the separation between ticks)
  which results in readable tick labels. The number of ticks that should be
  approximately generated can be set via \ref setTickCount. Depending on the
  current tick step strategy (\ref setTickStepStrategy), the algorithm either
  sacrifices readability to better match the specified tick count (\ref
  BaseChartAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of
  better tick steps (\ref BaseChartAxisTicker::tssReadability), which is the
  default.

  The following more specialized axis ticker subclasses are available, see
  details in the respective class documentation:

  <center>
  <table>
  <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerFixed</td><td>\image html
  axisticker-fixed.png</td></tr> <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerLog</td><td>\image html axisticker-log.png</td></tr>
  <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerPi</td><td>\image html axisticker-pi.png</td></tr>
  <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerText</td><td>\image html axisticker-text.png</td></tr>
  <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerDateTime</td><td>\image html
  axisticker-datetime.png</td></tr> <tr><td style="text-align:right; padding: 0
  1em">BaseChartAxisTickerTime</td><td>\image html axisticker-time.png \image
  html axisticker-time2.png</td></tr>
  </table>
  </center>

  \section axisticker-subclassing Creating own axis tickers

  Creating own axis tickers can be achieved very easily by sublassing
  BaseChartAxisTicker and reimplementing some or all of the available virtual
  methods.

  In the simplest case you might wish to just generate different tick steps than
  the other tickers, so you only reimplement the method \ref getTickStep. If you
  additionally want control over the string that will be shown as tick label,
  reimplement \ref getTickLabel.

  If you wish to have complete control, you can generate the tick vectors and
  tick label vectors yourself by reimplementing \ref createTickVector and \ref
  createLabelVector. The default implementations use the previously mentioned
  virtual methods \ref getTickStep and \ref getTickLabel, but your
  reimplementations don't necessarily need to do so. For example in the case of
  unequal tick steps, the method \ref getTickStep loses its usefulness and can
  be ignored.

  The sub tick count between major ticks can be controlled with \ref
  getSubTickCount. Full sub tick placement control is obtained by reimplementing
  \ref createSubTickVector.

  See the documentation of all these virtual methods in BaseChartAxisTicker for
  detailed information about the parameters and expected return values.
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTicker::BaseChartAxisTicker()
    : mTickStepStrategy(tssReadability), mTickCount(5), mTickOrigin(0) {}

BaseChartAxisTicker::~BaseChartAxisTicker() {}

/*!
  Sets which strategy the axis ticker follows when choosing the size of the tick
  step. For the available strategies, see \ref TickStepStrategy.
*/
void BaseChartAxisTicker::setTickStepStrategy(
    BaseChartAxisTicker::TickStepStrategy strategy) {
  mTickStepStrategy = strategy;
}

/*!
  Sets how many ticks this ticker shall aim to generate across the axis range.
  Note that \a count is not guaranteed to be matched exactly, as generating
  readable tick intervals may conflict with the requested number of ticks.

  Whether the readability has priority over meeting the requested \a count can
  be specified with \ref setTickStepStrategy.
*/
void BaseChartAxisTicker::setTickCount(int count) {
  if (count > 0)
    mTickCount = count;
  else
    qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
}

/*!
  Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick
  coordinate is just a concept and doesn't need to be inside the currently
  visible axis range.

  By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10,
  15,...} when the tick step is five. If \a origin is now set to 1 instead, the
  correspondingly generated ticks would be
  {-4, 1, 6, 11, 16,...}.
*/
void BaseChartAxisTicker::setTickOrigin(double origin) { mTickOrigin = origin; }

/*!
  This is the method called by BaseChartAxis in order to actually generate tick
  coordinates (\a ticks), tick label strings (\a tickLabels) and sub tick
  coordinates (\a subTicks).

  The ticks are generated for the specified \a range. The generated labels
  typically follow the specified \a locale, \a formatChar and number \a
  precision, however this might be different (or even irrelevant) for certain
  BaseChartAxisTicker subclasses.

  The output parameter \a ticks is filled with the generated tick positions in
  axis coordinates. The output parameters \a subTicks and \a tickLabels are
  optional (set them to \c nullptr if not needed) and are respectively filled
  with sub tick coordinates, and tick label strings belonging to \a ticks by
  index.
*/
void BaseChartAxisTicker::generate(const BaseChartRange& range,
                                   const QLocale& locale, QChar formatChar,
                                   int precision, QVector<double>& ticks,
                                   QVector<double>* subTicks,
                                   QVector<QString>* tickLabels) {
  // generate (major) ticks:
  double tickStep = getTickStep(range);
  ticks = createTickVector(tickStep, range);
  trimTicks(range, ticks,
            true);  // trim ticks to visible range plus one outer tick on each
                    // side (incase a subclass createTickVector creates more)

  // generate sub ticks between major ticks:
  if (subTicks) {
    if (!ticks.isEmpty()) {
      *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
      trimTicks(range, *subTicks, false);
    } else
      *subTicks = QVector<double>();
  }

  // finally trim also outliers (no further clipping happens in axis drawing):
  trimTicks(range, ticks, false);
  // generate labels for visible ticks if requested:
  if (tickLabels)
    *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
}

/*! \internal

  Takes the entire currently visible axis range and returns a sensible tick step
  in order to provide readable tick labels as well as a reasonable number of
  tick counts (see \ref setTickCount, \ref setTickStepStrategy).

  If a BaseChartAxisTicker subclass only wants a different tick step behaviour
  than the default implementation, it should reimplement this method. See \ref
  cleanMantissa for a possible helper function.
*/
double BaseChartAxisTicker::getTickStep(const BaseChartRange& range) {
  double exactStep =
      range.size() /
      double(mTickCount +
             1e-10);  // mTickCount ticks on average, the small addition is to
                      // prevent jitter on exact integers
  return cleanMantissa(exactStep);
}

/*! \internal

  Takes the \a tickStep, i.e. the distance between two consecutive ticks, and
  returns an appropriate number of sub ticks for that specific tick step.

  Note that a returned sub tick count of e.g. 4 will split each tick interval
  into 5 sections.
*/
int BaseChartAxisTicker::getSubTickCount(double tickStep) {
  int result = 1;  // default to 1, if no proper value can be found

  // separate integer and fractional part of mantissa:
  double epsilon = 0.01;
  double intPartf;
  int intPart;
  double fracPart = modf(getMantissa(tickStep), &intPartf);
  intPart = int(intPartf);

  // handle cases with (almost) integer mantissa:
  if (fracPart < epsilon || 1.0 - fracPart < epsilon) {
    if (1.0 - fracPart < epsilon) ++intPart;
    switch (intPart) {
      case 1:
        result = 4;
        break;  // 1.0 -> 0.2 substep
      case 2:
        result = 3;
        break;  // 2.0 -> 0.5 substep
      case 3:
        result = 2;
        break;  // 3.0 -> 1.0 substep
      case 4:
        result = 3;
        break;  // 4.0 -> 1.0 substep
      case 5:
        result = 4;
        break;  // 5.0 -> 1.0 substep
      case 6:
        result = 2;
        break;  // 6.0 -> 2.0 substep
      case 7:
        result = 6;
        break;  // 7.0 -> 1.0 substep
      case 8:
        result = 3;
        break;  // 8.0 -> 2.0 substep
      case 9:
        result = 2;
        break;  // 9.0 -> 3.0 substep
    }
  } else {
    // handle cases with significantly fractional mantissa:
    if (qAbs(fracPart - 0.5) < epsilon)  // *.5 mantissa
    {
      switch (intPart) {
        case 1:
          result = 2;
          break;  // 1.5 -> 0.5 substep
        case 2:
          result = 4;
          break;  // 2.5 -> 0.5 substep
        case 3:
          result = 4;
          break;  // 3.5 -> 0.7 substep
        case 4:
          result = 2;
          break;  // 4.5 -> 1.5 substep
        case 5:
          result = 4;
          break;  // 5.5 -> 1.1 substep (won't occur with default getTickStep
                  // from here on)
        case 6:
          result = 4;
          break;  // 6.5 -> 1.3 substep
        case 7:
          result = 2;
          break;  // 7.5 -> 2.5 substep
        case 8:
          result = 4;
          break;  // 8.5 -> 1.7 substep
        case 9:
          result = 4;
          break;  // 9.5 -> 1.9 substep
      }
    }
    // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick
    // marks, leave default
  }

  return result;
}

/*! \internal

  This method returns the tick label string as it should be printed under the \a
  tick coordinate. If a textual number is returned, it should respect the
  provided \a locale, \a formatChar and \a precision.

  If the returned value contains exponentials of the form "2e5" and beautifully
  typeset powers is enabled in the BaseChartAxis number format (\ref
  BaseChartAxis::setNumberFormat), the exponential part will be formatted
  accordingly using multiplication symbol and superscript during rendering of
  the label automatically.
*/
QString BaseChartAxisTicker::getTickLabel(double tick, const QLocale& locale,
                                          QChar formatChar, int precision) {
  return locale.toString(tick, formatChar.toLatin1(), precision);
}

/*! \internal

  Returns a vector containing all coordinates of sub ticks that should be drawn.
  It generates \a subTickCount sub ticks between each tick pair given in \a
  ticks.

  If a BaseChartAxisTicker subclass needs maximal control over the generated sub
  ticks, it should reimplement this method. Depending on the purpose of the
  subclass it doesn't necessarily need to base its result on \a subTickCount or
  \a ticks.
*/
QVector<double> BaseChartAxisTicker::createSubTickVector(
    int subTickCount, const QVector<double>& ticks) {
  QVector<double> result;
  if (subTickCount <= 0 || ticks.size() < 2) return result;

  result.reserve((ticks.size() - 1) * subTickCount);
  for (int i = 1; i < ticks.size(); ++i) {
    double subTickStep =
        (ticks.at(i) - ticks.at(i - 1)) / double(subTickCount + 1);
    for (int k = 1; k <= subTickCount; ++k)
      result.append(ticks.at(i - 1) + k * subTickStep);
  }
  return result;
}

/*! \internal

  Returns a vector containing all coordinates of ticks that should be drawn. The
  default implementation generates ticks with a spacing of \a tickStep
  (mathematically starting at the tick step origin, see \ref setTickOrigin)
  distributed over the passed \a range.

  In order for the axis ticker to generate proper sub ticks, it is necessary
  that the first and last tick coordinates returned by this method are just
  below/above the provided \a range. Otherwise the outer intervals won't contain
  any sub ticks.

  If a BaseChartAxisTicker subclass needs maximal control over the generated
  ticks, it should reimplement this method. Depending on the purpose of the
  subclass it doesn't necessarily need to base its result on \a tickStep, e.g.
  when the ticks are spaced unequally like in the case of
  BaseChartAxisTickerLog.
*/
QVector<double> BaseChartAxisTicker::createTickVector(
    double tickStep, const BaseChartRange& range) {
  QVector<double> result;
  // Generate tick positions according to tickStep:
  qint64 firstStep = qint64(floor(
      (range.lower - mTickOrigin) /
      tickStep));  // do not use qFloor here, or we'll lose 64 bit precision
  qint64 lastStep = qint64(
      ceil((range.upper - mTickOrigin) /
           tickStep));  // do not use qCeil here, or we'll lose 64 bit precision
  int tickcount = int(lastStep - firstStep + 1);
  if (tickcount < 0) tickcount = 0;
  result.resize(tickcount);
  for (int i = 0; i < tickcount; ++i)
    result[i] = mTickOrigin + (firstStep + i) * tickStep;
  return result;
}

/*! \internal

  Returns a vector containing all tick label strings corresponding to the tick
  coordinates provided in \a ticks. The default implementation calls \ref
  getTickLabel to generate the respective strings.

  It is possible but uncommon for BaseChartAxisTicker subclasses to reimplement
  this method, as reimplementing \ref getTickLabel often achieves the intended
  result easier.
*/
QVector<QString> BaseChartAxisTicker::createLabelVector(
    const QVector<double>& ticks, const QLocale& locale, QChar formatChar,
    int precision) {
  QVector<QString> result;
  result.reserve(ticks.size());
  foreach (double tickCoord, ticks)
    result.append(getTickLabel(tickCoord, locale, formatChar, precision));
  return result;
}

/*! \internal

  Removes tick coordinates from \a ticks which lie outside the specified \a
  range. If \a keepOneOutlier is true, it preserves one tick just outside the
  range on both sides, if present.

  The passed \a ticks must be sorted in ascending order.
*/
void BaseChartAxisTicker::trimTicks(const BaseChartRange& range,
                                    QVector<double>& ticks,
                                    bool keepOneOutlier) const {
  bool lowFound = false;
  bool highFound = false;
  int lowIndex = 0;
  int highIndex = -1;

  for (int i = 0; i < ticks.size(); ++i) {
    if (ticks.at(i) >= range.lower) {
      lowFound = true;
      lowIndex = i;
      break;
    }
  }
  for (int i = ticks.size() - 1; i >= 0; --i) {
    if (ticks.at(i) <= range.upper) {
      highFound = true;
      highIndex = i;
      break;
    }
  }

  if (highFound && lowFound) {
    int trimFront = qMax(0, lowIndex - (keepOneOutlier ? 1 : 0));
    int trimBack = qMax(0, ticks.size() - (keepOneOutlier ? 2 : 1) - highIndex);
    if (trimFront > 0 || trimBack > 0)
      ticks = ticks.mid(trimFront, ticks.size() - trimFront - trimBack);
  } else  // all ticks are either all below or all above the range
    ticks.clear();
}

/*! \internal

  Returns the coordinate contained in \a candidates which is closest to the
  provided \a target.

  This method assumes \a candidates is not empty and sorted in ascending order.
*/
double BaseChartAxisTicker::pickClosest(
    double target, const QVector<double>& candidates) const {
  if (candidates.size() == 1) return candidates.first();
  QVector<double>::const_iterator it =
      std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
  if (it == candidates.constEnd())
    return *(it - 1);
  else if (it == candidates.constBegin())
    return *it;
  else
    return target - *(it - 1) < *it - target ? *(it - 1) : *it;
}

/*! \internal

  Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not
  set to zero, it also returns the magnitude of \a input as a power of 10.

  For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude
  of 100.
*/
double BaseChartAxisTicker::getMantissa(double input, double* magnitude) const {
  const double mag = qPow(10.0, qFloor(qLn(input) / qLn(10.0)));
  if (magnitude) *magnitude = mag;
  return input / mag;
}

/*! \internal

  Returns a number that is close to \a input but has a clean, easier human
  readable mantissa. How strongly the mantissa is altered, and thus how strong
  the result deviates from the original \a input, depends on the current tick
  step strategy (see \ref setTickStepStrategy).
*/
double BaseChartAxisTicker::cleanMantissa(double input) const {
  double magnitude;
  const double mantissa = getMantissa(input, &magnitude);
  switch (mTickStepStrategy) {
    case tssReadability: {
      return pickClosest(mantissa, QVector<double>()
                                       << 1.0 << 2.0 << 2.5 << 5.0 << 10.0) *
             magnitude;
    }
    case tssMeetTickCount: {
      // this gives effectively a mantissa
      // of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0
      if (mantissa <= 5.0)
        return int(mantissa * 2) / 2.0 *
               magnitude;  // round digit after decimal point to 0.5
      else
        return int(mantissa / 2.0) * 2.0 *
               magnitude;  // round to first digit in multiples of 2
    }
  }
  return input;
}
/* end of 'src/axis/axisticker.cpp' */

/* including file 'src/axis/axistickerdatetime.cpp' */
/* modified 2021-03-29T02:30:44, size 18829         */

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerDateTime
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerDateTime
  \brief Specialized axis ticker for calendar dates and times as axis ticks

  \image html axisticker-datetime.png

  This BaseChartAxisTicker subclass generates ticks that correspond to real
  calendar dates and times. The plot axis coordinate is interpreted as Unix
  Time, so seconds since Epoch (January 1, 1970, 00:00 UTC). This is also used
  for example by QDateTime in the <tt>toTime_t()/setTime_t()</tt> methods with a
  precision of one second. Since Qt 4.7, millisecond accuracy can be obtained
  from QDateTime by using <tt>QDateTime::fromMSecsSinceEpoch()/1000.0</tt>. The
  static methods \ref dateTimeToKey and \ref keyToDateTime conveniently perform
  this conversion achieving a precision of one millisecond on all Qt versions.

  The format of the date/time display in the tick labels is controlled with \ref
  setDateTimeFormat. If a different time spec or time zone shall be used for the
  tick label appearance, see \ref setDateTimeSpec or \ref setTimeZone,
  respectively.

  This ticker produces unequal tick spacing in order to provide intuitive date
  and time-of-day ticks. For example, if the axis range spans a few years such
  that there is one tick per year, ticks will be positioned on 1. January of
  every year. This is intuitive but, due to leap years, will result in slightly
  unequal tick intervals (visually unnoticeable). The same can be seen in the
  image above: even though the number of days varies month by month, this ticker
  generates ticks on the same day of each month.

  If you would like to change the date/time that is used as a (mathematical)
  starting date for the ticks, use the \ref setTickOrigin(const QDateTime
  &origin) method overload, which takes a QDateTime. If you pass 15. July, 9:45
  to this method, the yearly ticks will end up on 15. July at 9:45 of every
  year.

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickerdatetime-creation

  \note If you rather wish to display relative times in terms of days, hours,
  minutes, seconds and milliseconds, and are not interested in the intricacies
  of real calendar dates with months and (leap) years, have a look at
  BaseChartAxisTickerTime instead.
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerDateTime::BaseChartAxisTickerDateTime()
    : mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
      mDateTimeSpec(Qt::LocalTime),
      mDateStrategy(dsNone) {
  setTickCount(4);
}

/*!
  Sets the format in which dates and times are displayed as tick labels. For
  details about the \a format string, see the documentation of
  QDateTime::toString().

  Typical expressions are
  <table>
    <tr><td>\c d</td><td>The day as a number without a leading zero (1 to
  31)</td></tr> <tr><td>\c dd</td><td>The day as a number with a leading zero
  (01 to 31)</td></tr> <tr><td>\c ddd</td><td>The abbreviated localized day name
  (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e.
  QLocale::system().</td></tr> <tr><td>\c dddd</td><td>The long localized day
  name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name,
  i.e. QLocale::system().</td></tr> <tr><td>\c M</td><td>The month as a number
  without a leading zero (1 to 12)</td></tr> <tr><td>\c MM</td><td>The month as
  a number with a leading zero (01 to 12)</td></tr> <tr><td>\c MMM</td><td>The
  abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale
  to localize the name, i.e. QLocale::system().</td></tr> <tr><td>\c
  MMMM</td><td>The long localized month name (e.g. 'January' to 'December').
  Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>
    <tr><td>\c yy</td><td>The year as a two digit number (00 to 99)</td></tr>
    <tr><td>\c yyyy</td><td>The year as a four digit number. If the year is
  negative, a minus sign is prepended, making five characters.</td></tr>
    <tr><td>\c h</td><td>The hour without a leading zero (0 to 23 or 1 to 12 if
  AM/PM display)</td></tr> <tr><td>\c hh</td><td>The hour with a leading zero
  (00 to 23 or 01 to 12 if AM/PM display)</td></tr> <tr><td>\c H</td><td>The
  hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>
    <tr><td>\c HH</td><td>The hour with a leading zero (00 to 23, even with
  AM/PM display)</td></tr> <tr><td>\c m</td><td>The minute without a leading
  zero (0 to 59)</td></tr> <tr><td>\c mm</td><td>The minute with a leading zero
  (00 to 59)</td></tr> <tr><td>\c s</td><td>The whole second, without any
  leading zero (0 to 59)</td></tr> <tr><td>\c ss</td><td>The whole second, with
  a leading zero where applicable (00 to 59)</td></tr> <tr><td>\c z</td><td>The
  fractional part of the second, to go after a decimal point, without trailing
  zeroes (0 to 999). Thus "s.z" reports the seconds to full available
  (millisecond) precision without trailing zeroes.</td></tr> <tr><td>\c
  zzz</td><td>The fractional part of the second, to millisecond precision,
  including trailing zeroes where applicable (000 to 999).</td></tr> <tr><td>\c
  AP or \c A</td><td>Use AM/PM display. A/AP will be replaced by an upper-case
  version of either QLocale::amText() or QLocale::pmText().</td></tr> <tr><td>\c
  ap or \c a</td><td>Use am/pm display. a/ap will be replaced by a lower-case
  version of either QLocale::amText() or QLocale::pmText().</td></tr> <tr><td>\c
  t</td><td>The timezone (for example "CEST")</td></tr>
  </table>

  Newlines can be inserted with \c "\n", literal strings (even when containing
  above expressions) by encapsulating them using single-quotes. A literal single
  quote can be generated by using two consecutive single quotes in the format.

  \see setDateTimeSpec, setTimeZone
*/
void BaseChartAxisTickerDateTime::setDateTimeFormat(const QString& format) {
  mDateTimeFormat = format;
}

/*!
  Sets the time spec that is used for creating the tick labels from
  corresponding dates/times.

  The default value of QDateTime objects (and also BaseChartAxisTickerDateTime)
  is <tt>Qt::LocalTime</tt>. However, if the displayed tick labels shall be
  given in UTC, set \a spec to <tt>Qt::UTC</tt>.

  Tick labels corresponding to other time zones can be achieved with \ref
  setTimeZone (which sets \a spec to \c Qt::TimeZone internally). Note that if
  \a spec is afterwards set to not be \c Qt::TimeZone again, the \ref
  setTimeZone setting will be ignored accordingly.

  \see setDateTimeFormat, setTimeZone
*/
void BaseChartAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec) {
  mDateTimeSpec = spec;
}

#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
/*!
  Sets the time zone that is used for creating the tick labels from
  corresponding dates/times. The time spec (\ref setDateTimeSpec) is set to \c
  Qt::TimeZone.

  \see setDateTimeFormat, setTimeZone
*/
void BaseChartAxisTickerDateTime::setTimeZone(const QTimeZone& zone) {
  mTimeZone = zone;
  mDateTimeSpec = Qt::TimeZone;
}
#endif

/*!
  Sets the tick origin (see \ref BaseChartAxisTicker::setTickOrigin) in seconds
  since Epoch (1. Jan 1970, 00:00 UTC). For the date time ticker it might be
  more intuitive to use the overload which directly takes a QDateTime, see \ref
  setTickOrigin(const QDateTime &origin).

  This is useful to define the month/day/time recurring at greater tick interval
  steps. For example, If you pass 15. July, 9:45 to this method and the tick
  interval happens to be one tick per year, the ticks will end up on 15. July at
  9:45 of every year.
*/
void BaseChartAxisTickerDateTime::setTickOrigin(double origin) {
  BaseChartAxisTicker::setTickOrigin(origin);
}

/*!
  Sets the tick origin (see \ref BaseChartAxisTicker::setTickOrigin) as a
  QDateTime \a origin.

  This is useful to define the month/day/time recurring at greater tick interval
  steps. For example, If you pass 15. July, 9:45 to this method and the tick
  interval happens to be one tick per year, the ticks will end up on 15. July at
  9:45 of every year.
*/
void BaseChartAxisTickerDateTime::setTickOrigin(const QDateTime& origin) {
  setTickOrigin(dateTimeToKey(origin));
}

/*! \internal

  Returns a sensible tick step with intervals appropriate for a
  date-time-display, such as weekly, monthly, bi-monthly, etc.

  Note that this tick step isn't used exactly when generating the tick vector in
  \ref createTickVector, but only as a guiding value requiring some correction
  for each individual tick interval. Otherwise this would lead to unintuitive
  date displays, e.g. jumping between first day in the month to the last day in
  the previous month from tick to tick, due to the non-uniform length of months.
  The same problem arises with leap years.

  \seebaseclassmethod
*/
double BaseChartAxisTickerDateTime::getTickStep(const BaseChartRange& range) {
  double result =
      range.size() /
      double(mTickCount +
             1e-10);  // mTickCount ticks on average, the small addition is to
                      // prevent jitter on exact integers

  mDateStrategy =
      dsNone;  // leaving it at dsNone means tick coordinates will not be tuned
               // in any special way in createTickVector
  if (result < 1)  // ideal tick step is below 1 second -> use normal clean
                   // mantissa algorithm in units of seconds
  {
    result = cleanMantissa(result);
  } else if (result < 86400 * 30.4375 * 12)  // below a year
  {
    result = pickClosest(
        result, QVector<double>()
                    << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5 * 60
                    << 5 * 60 << 10 * 60 << 15 * 60 << 30 * 60
                    << 60 * 60  // second, minute, hour range
                    << 3600 * 2 << 3600 * 3 << 3600 * 6 << 3600 * 12
                    << 3600 * 24  // hour to day range
                    << 86400 * 2 << 86400 * 5 << 86400 * 7 << 86400 * 14
                    << 86400 * 30.4375 << 86400 * 30.4375 * 2
                    << 86400 * 30.4375 * 3 << 86400 * 30.4375 * 6
                    << 86400 * 30.4375 *
                           12);  // day, week, month range (avg. days per month
                                 // includes leap years)
    if (result > 86400 * 30.4375 - 1)  // month tick intervals or larger
      mDateStrategy = dsUniformDayInMonth;
    else if (result > 3600 * 24 - 1)  // day tick intervals or larger
      mDateStrategy = dsUniformTimeInDay;
  } else  // more than a year, go back to normal clean mantissa algorithm but in
          // units of years
  {
    const double secondsPerYear =
        86400 * 30.4375 * 12;  // average including leap years
    result = cleanMantissa(result / secondsPerYear) * secondsPerYear;
    mDateStrategy = dsUniformDayInMonth;
  }
  return result;
}

/*! \internal

  Returns a sensible sub tick count with intervals appropriate for a
  date-time-display, such as weekly, monthly, bi-monthly, etc.

  \seebaseclassmethod
*/
int BaseChartAxisTickerDateTime::getSubTickCount(double tickStep) {
  int result = BaseChartAxisTicker::getSubTickCount(tickStep);
  switch (qRound(tickStep))  // hand chosen subticks for specific
                             // minute/hour/day/week/month range (as specified
                             // in getTickStep)
  {
    case 5 * 60:
      result = 4;
      break;
    case 10 * 60:
      result = 1;
      break;
    case 15 * 60:
      result = 2;
      break;
    case 30 * 60:
      result = 1;
      break;
    case 60 * 60:
      result = 3;
      break;
    case 3600 * 2:
      result = 3;
      break;
    case 3600 * 3:
      result = 2;
      break;
    case 3600 * 6:
      result = 1;
      break;
    case 3600 * 12:
      result = 3;
      break;
    case 3600 * 24:
      result = 3;
      break;
    case 86400 * 2:
      result = 1;
      break;
    case 86400 * 5:
      result = 4;
      break;
    case 86400 * 7:
      result = 6;
      break;
    case 86400 * 14:
      result = 1;
      break;
    case int(86400 * 30.4375 + 0.5):
      result = 3;
      break;
    case int(86400 * 30.4375 * 2 + 0.5):
      result = 1;
      break;
    case int(86400 * 30.4375 * 3 + 0.5):
      result = 2;
      break;
    case int(86400 * 30.4375 * 6 + 0.5):
      result = 5;
      break;
    case int(86400 * 30.4375 * 12 + 0.5):
      result = 3;
      break;
  }
  return result;
}

/*! \internal

  Generates a date/time tick label for tick coordinate \a tick, based on the
  currently set format
  (\ref setDateTimeFormat), time spec (\ref setDateTimeSpec), and possibly time
  zone (\ref setTimeZone).

  \seebaseclassmethod
*/
QString BaseChartAxisTickerDateTime::getTickLabel(double tick,
                                                  const QLocale& locale,
                                                  QChar formatChar,
                                                  int precision) {
  Q_UNUSED(precision)
  Q_UNUSED(formatChar)

  if (mDateTimeSpec == Qt::TimeZone)
    return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone),
                           mDateTimeFormat);
  else
    return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec),
                           mDateTimeFormat);
}

/*! \internal

  Uses the passed \a tickStep as a guiding value and applies corrections in
  order to obtain non-uniform tick intervals but intuitive tick labels, e.g.
  falling on the same day of each month.

  \seebaseclassmethod
*/
QVector<double> BaseChartAxisTickerDateTime::createTickVector(
    double tickStep, const BaseChartRange& range) {
  QVector<double> result =
      BaseChartAxisTicker::createTickVector(tickStep, range);
  if (!result.isEmpty()) {
    if (mDateStrategy == dsUniformTimeInDay) {
      QDateTime uniformDateTime =
          keyToDateTime(mTickOrigin);  // the time of this datetime will be set
                                       // for all other ticks, if possible
      QDateTime tickDateTime;
      for (int i = 0; i < result.size(); ++i) {
        tickDateTime = keyToDateTime(result.at(i));
        tickDateTime.setTime(uniformDateTime.time());
        result[i] = dateTimeToKey(tickDateTime);
      }
    } else if (mDateStrategy == dsUniformDayInMonth) {
      QDateTime uniformDateTime =
          keyToDateTime(mTickOrigin);  // this day (in month) and time will be
                                       // set for all other ticks, if possible
      QDateTime tickDateTime;
      for (int i = 0; i < result.size(); ++i) {
        tickDateTime = keyToDateTime(result.at(i));
        tickDateTime.setTime(uniformDateTime.time());
        int thisUniformDay =
            uniformDateTime.date().day() <= tickDateTime.date().daysInMonth()
                ? uniformDateTime.date().day()
                : tickDateTime.date()
                      .daysInMonth();  // don't exceed month (e.g. try to set
                                       // day 31 in February)
        if (thisUniformDay - tickDateTime.date().day() <
            -15)  // with leap years involved, date month may jump backwards or
                  // forwards, and needs to be corrected before setting day
          tickDateTime = tickDateTime.addMonths(1);
        else if (thisUniformDay - tickDateTime.date().day() >
                 15)  // with leap years involved, date month may jump backwards
                      // or forwards, and needs to be corrected before setting
                      // day
          tickDateTime = tickDateTime.addMonths(-1);
        tickDateTime.setDate(QDate(tickDateTime.date().year(),
                                   tickDateTime.date().month(),
                                   thisUniformDay));
        result[i] = dateTimeToKey(tickDateTime);
      }
    }
  }
  return result;
}

/*!
  A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970,
  00:00 UTC) into a QDateTime object. This can be used to turn axis coordinates
  to actual QDateTimes.

  The accuracy achieved by this method is one millisecond, irrespective of the
  used Qt version (it works around the lack of a QDateTime::fromMSecsSinceEpoch
  in Qt 4.6)

  \see dateTimeToKey
*/
QDateTime BaseChartAxisTickerDateTime::keyToDateTime(double key) {
  return QDateTime::fromMSecsSinceEpoch(qint64(key * 1000.0));
}

/*! \overload

  A convenience method which turns a QDateTime object into a double value that
  corresponds to seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the
  format used as axis coordinates by BaseChartAxisTickerDateTime.

  The accuracy achieved by this method is one millisecond, irrespective of the
  used Qt version (it works around the lack of a QDateTime::toMSecsSinceEpoch in
  Qt 4.6)

  \see keyToDateTime
*/
double BaseChartAxisTickerDateTime::dateTimeToKey(const QDateTime& dateTime) {
  return dateTime.toMSecsSinceEpoch() / 1000.0;
}

/*! \overload

  A convenience method which turns a QDate object into a double value that
  corresponds to seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the
  format used as axis coordinates by BaseChartAxisTickerDateTime.

  The returned value will be the start of the passed day of \a date, interpreted
  in the given \a timeSpec.

  \see keyToDateTime
*/
double BaseChartAxisTickerDateTime::dateTimeToKey(const QDate& date,
                                                  Qt::TimeSpec timeSpec) {
  return date.startOfDay(timeSpec).toMSecsSinceEpoch() / 1000.0;
}
/* end of 'src/axis/axistickerdatetime.cpp' */

/* including file 'src/axis/axistickertime.cpp' */
/* modified 2021-03-29T02:30:44, size 11745     */

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerTime
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerTime
  \brief Specialized axis ticker for time spans in units of milliseconds to days

  \image html axisticker-time.png

  This BaseChartAxisTicker subclass generates ticks that corresponds to time
  intervals.

  The format of the time display in the tick labels is controlled with \ref
  setTimeFormat and \ref setFieldWidth. The time coordinate is in the unit of
  seconds with respect to the time coordinate zero. Unlike with
  BaseChartAxisTickerDateTime, the ticks don't correspond to a specific calendar
  date and time.

  The time can be displayed in milliseconds, seconds, minutes, hours and days.
  Depending on the largest available unit in the format specified with \ref
  setTimeFormat, any time spans above will be carried in that largest unit. So
  for example if the format string is "%m:%s" and a tick at coordinate value
  7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick
  label will show "130:15" (130 minutes, 15 seconds). If the format string is
  "%h:%m:%s", the hour unit will be used and the label will thus be "02:10:15".
  Negative times with respect to the axis zero will carry a leading minus sign.

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickertime-creation

  Here is an example of a time axis providing time information in days, hours
  and minutes. Due to the axis range spanning a few days and the wanted tick
  count (\ref setTickCount), the ticker decided to use tick steps of 12 hours:

  \image html axisticker-time2.png

  The format string for this example is
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickertime-creation-2

  \note If you rather wish to display calendar dates and times, have a look at
  BaseChartAxisTickerDateTime instead.
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerTime::BaseChartAxisTickerTime()
    : mTimeFormat(QLatin1String("%h:%m:%s")),
      mSmallestUnit(tuSeconds),
      mBiggestUnit(tuHours) {
  setTickCount(4);
  mFieldWidth[tuMilliseconds] = 3;
  mFieldWidth[tuSeconds] = 2;
  mFieldWidth[tuMinutes] = 2;
  mFieldWidth[tuHours] = 2;
  mFieldWidth[tuDays] = 1;

  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
  mFormatPattern[tuSeconds] = QLatin1String("%s");
  mFormatPattern[tuMinutes] = QLatin1String("%m");
  mFormatPattern[tuHours] = QLatin1String("%h");
  mFormatPattern[tuDays] = QLatin1String("%d");
}

/*!
  Sets the format that will be used to display time in the tick labels.

  The available patterns are:
  - %%z for milliseconds
  - %%s for seconds
  - %%m for minutes
  - %%h for hours
  - %%d for days

  The field width (zero padding) can be controlled for each unit with \ref
  setFieldWidth.

  The largest unit that appears in \a format will carry all the remaining time
  of a certain tick coordinate, even if it overflows the natural limit of the
  unit. For example, if %%m is the largest unit it might become larger than 59
  in order to consume larger time values. If on the other hand %%h is available,
  the minutes will wrap around to zero after 59 and the time will carry to the
  hour digit.
*/
void BaseChartAxisTickerTime::setTimeFormat(const QString& format) {
  mTimeFormat = format;

  // determine smallest and biggest unit in format, to optimize unit replacement
  // and allow biggest unit to consume remaining time of a tick value and grow
  // beyond its modulo (e.g. min > 59)
  mSmallestUnit = tuMilliseconds;
  mBiggestUnit = tuMilliseconds;
  bool hasSmallest = false;
  for (int i = tuMilliseconds; i <= tuDays; ++i) {
    TimeUnit unit = static_cast<TimeUnit>(i);
    if (mTimeFormat.contains(mFormatPattern.value(unit))) {
      if (!hasSmallest) {
        mSmallestUnit = unit;
        hasSmallest = true;
      }
      mBiggestUnit = unit;
    }
  }
}

/*!
  Sets the field widh of the specified \a unit to be \a width digits, when
  displayed in the tick label. If the number for the specific unit is shorter
  than \a width, it will be padded with an according number of zeros to the left
  in order to reach the field width.

  \see setTimeFormat
*/
void BaseChartAxisTickerTime::setFieldWidth(
    BaseChartAxisTickerTime::TimeUnit unit, int width) {
  mFieldWidth[unit] = qMax(width, 1);
}

/*! \internal

  Returns the tick step appropriate for time displays, depending on the provided
  \a range and the smallest available time unit in the current format (\ref
  setTimeFormat). For example if the unit of seconds isn't available in the
  format, this method will not generate steps (like 2.5 minutes) that require
  sub-minute precision to be displayed correctly.

  \seebaseclassmethod
*/
double BaseChartAxisTickerTime::getTickStep(const BaseChartRange& range) {
  double result =
      range.size() /
      double(mTickCount +
             1e-10);  // mTickCount ticks on average, the small addition is to
                      // prevent jitter on exact integers

  if (result < 1)  // ideal tick step is below 1 second -> use normal clean
                   // mantissa algorithm in units of seconds
  {
    if (mSmallestUnit == tuMilliseconds)
      result = qMax(cleanMantissa(result),
                    0.001);  // smallest tick step is 1 millisecond
    else  // have no milliseconds available in format, so stick with 1 second
          // tickstep
      result = 1.0;
  } else if (result < 3600 * 24)  // below a day
  {
    // the filling of availableSteps seems a bit contorted but it fills in a
    // sorted fashion and thus saves a post-fill sorting run
    QVector<double> availableSteps;
    // seconds range:
    if (mSmallestUnit <= tuSeconds) availableSteps << 1;
    if (mSmallestUnit == tuMilliseconds)
      availableSteps << 2.5;  // only allow half second steps if milliseconds
                              // are there to display it
    else if (mSmallestUnit == tuSeconds)
      availableSteps << 2;
    if (mSmallestUnit <= tuSeconds) availableSteps << 5 << 10 << 15 << 30;
    // minutes range:
    if (mSmallestUnit <= tuMinutes) availableSteps << 1 * 60;
    if (mSmallestUnit <= tuSeconds)
      availableSteps << 2.5 * 60;  // only allow half minute steps if seconds
                                   // are there to display it
    else if (mSmallestUnit == tuMinutes)
      availableSteps << 2 * 60;
    if (mSmallestUnit <= tuMinutes)
      availableSteps << 5 * 60 << 10 * 60 << 15 * 60 << 30 * 60;
    // hours range:
    if (mSmallestUnit <= tuHours)
      availableSteps << 1 * 3600 << 2 * 3600 << 3 * 3600 << 6 * 3600
                     << 12 * 3600 << 24 * 3600;
    // pick available step that is most appropriate to approximate ideal step:
    result = pickClosest(result, availableSteps);
  } else  // more than a day, go back to normal clean mantissa algorithm but in
          // units of days
  {
    const double secondsPerDay = 3600 * 24;
    result = cleanMantissa(result / secondsPerDay) * secondsPerDay;
  }
  return result;
}

/*! \internal

  Returns the sub tick count appropriate for the provided \a tickStep and time
  displays.

  \seebaseclassmethod
*/
int BaseChartAxisTickerTime::getSubTickCount(double tickStep) {
  int result = BaseChartAxisTicker::getSubTickCount(tickStep);
  switch (
      qRound(tickStep))  // hand chosen subticks for specific minute/hour/day
                         // range (as specified in getTickStep)
  {
    case 5 * 60:
      result = 4;
      break;
    case 10 * 60:
      result = 1;
      break;
    case 15 * 60:
      result = 2;
      break;
    case 30 * 60:
      result = 1;
      break;
    case 60 * 60:
      result = 3;
      break;
    case 3600 * 2:
      result = 3;
      break;
    case 3600 * 3:
      result = 2;
      break;
    case 3600 * 6:
      result = 1;
      break;
    case 3600 * 12:
      result = 3;
      break;
    case 3600 * 24:
      result = 3;
      break;
  }
  return result;
}

/*! \internal

  Returns the tick label corresponding to the provided \a tick and the
  configured format and field widths (\ref setTimeFormat, \ref setFieldWidth).

  \seebaseclassmethod
*/
QString BaseChartAxisTickerTime::getTickLabel(double tick,
                                              const QLocale& locale,
                                              QChar formatChar, int precision) {
  Q_UNUSED(precision)
  Q_UNUSED(formatChar)
  Q_UNUSED(locale)
  bool negative = tick < 0;
  if (negative) tick *= -1;
  double values[tuDays + 1];  // contains the msec/sec/min/... value with its
                              // respective modulo (e.g. minute 0..59)
  double restValues[tuDays + 1];  // contains the msec/sec/min/... value as if
                                  // it's the largest available unit and thus
                                  // consumes the remaining time

  restValues[tuMilliseconds] = tick * 1000;
  values[tuMilliseconds] =
      modf(restValues[tuMilliseconds] / 1000, &restValues[tuSeconds]) * 1000;
  values[tuSeconds] =
      modf(restValues[tuSeconds] / 60, &restValues[tuMinutes]) * 60;
  values[tuMinutes] =
      modf(restValues[tuMinutes] / 60, &restValues[tuHours]) * 60;
  values[tuHours] = modf(restValues[tuHours] / 24, &restValues[tuDays]) * 24;
  // no need to set values[tuDays] because days are always a rest value (there
  // is no higher unit so it consumes all remaining time)

  QString result = mTimeFormat;
  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i) {
    TimeUnit iUnit = static_cast<TimeUnit>(i);
    replaceUnit(
        result, iUnit,
        qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
  }
  if (negative) result.prepend(QLatin1Char('-'));
  return result;
}

/*! \internal

  Replaces all occurrences of the format pattern belonging to \a unit in \a text
  with the specified \a value, using the field width as specified with \ref
  setFieldWidth for the \a unit.
*/
void BaseChartAxisTickerTime::replaceUnit(
    QString& text, BaseChartAxisTickerTime::TimeUnit unit, int value) const {
  QString valueStr = QString::number(value);
  while (valueStr.size() < mFieldWidth.value(unit))
    valueStr.prepend(QLatin1Char('0'));

  text.replace(mFormatPattern.value(unit), valueStr);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerFixed
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerFixed
  \brief Specialized axis ticker with a fixed tick step

  \image html axisticker-fixed.png

  This BaseChartAxisTicker subclass generates ticks with a fixed tick step set
  with \ref setTickStep. It is also possible to allow integer multiples and
  integer powers of the specified tick step with \ref setScaleStrategy.

  A typical application of this ticker is to make an axis only display integers,
  by setting the tick step of the ticker to 1.0 and the scale strategy to \ref
  ssMultiples.

  Another case is when a certain number has a special meaning and axis ticks
  should only appear at multiples of that value. In this case you might also
  want to consider \ref BaseChartAxisTickerPi because despite the name it is not
  limited to only pi symbols/values.

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickerfixed-creation
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerFixed::BaseChartAxisTickerFixed()
    : mTickStep(1.0), mScaleStrategy(ssNone) {}

/*!
  Sets the fixed tick interval to \a step.

  The axis ticker will only use this tick step when generating axis ticks. This
  might cause a very high tick density and overlapping labels if the axis range
  is zoomed out. Using \ref setScaleStrategy it is possible to relax the fixed
  step and also allow multiples or powers of \a step. This will enable the
  ticker to reduce the number of ticks to a reasonable amount (see \ref
  setTickCount).
*/
void BaseChartAxisTickerFixed::setTickStep(double step) {
  if (step > 0)
    mTickStep = step;
  else
    qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step;
}

/*!
  Sets whether the specified tick step (\ref setTickStep) is absolutely fixed or
  whether modifications may be applied to it before calculating the finally used
  tick step, such as permitting multiples or powers. See \ref ScaleStrategy for
  details.

  The default strategy is \ref ssNone, which means the tick step is absolutely
  fixed.
*/
void BaseChartAxisTickerFixed::setScaleStrategy(
    BaseChartAxisTickerFixed::ScaleStrategy strategy) {
  mScaleStrategy = strategy;
}

/*! \internal

  Determines the actually used tick step from the specified tick step and scale
  strategy (\ref setTickStep, \ref setScaleStrategy).

  This method either returns the specified tick step exactly, or, if the scale
  strategy is not \ref ssNone, a modification of it to allow varying the number
  of ticks in the current axis range.

  \seebaseclassmethod
*/
double BaseChartAxisTickerFixed::getTickStep(const BaseChartRange& range) {
  switch (mScaleStrategy) {
    case ssNone: {
      return mTickStep;
    }
    case ssMultiples: {
      double exactStep =
          range.size() /
          double(mTickCount +
                 1e-10);  // mTickCount ticks on average, the small addition is
                          // to prevent jitter on exact integers
      if (exactStep < mTickStep)
        return mTickStep;
      else
        return qint64(cleanMantissa(exactStep / mTickStep) + 0.5) * mTickStep;
    }
    case ssPowers: {
      double exactStep =
          range.size() /
          double(mTickCount +
                 1e-10);  // mTickCount ticks on average, the small addition is
                          // to prevent jitter on exact integers
      return qPow(mTickStep, int(qLn(exactStep) / qLn(mTickStep) + 0.5));
    }
  }
  return mTickStep;
}
/* end of 'src/axis/axistickerfixed.cpp' */

/* including file 'src/axis/axistickertext.cpp' */
/* modified 2021-03-29T02:30:44, size 8742      */

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerText
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerText
  \brief Specialized axis ticker which allows arbitrary labels at specified
  coordinates

  \image html axisticker-text.png

  This BaseChartAxisTicker subclass generates ticks which can be directly
  specified by the user as coordinates and associated strings. They can be
  passed as a whole with \ref setTicks or one at a time with \ref addTick.
  Alternatively you can directly access the internal storage via \ref ticks and
  modify the tick/label data there.

  This is useful for cases where the axis represents categories rather than
  numerical values.

  If you are updating the ticks of this ticker regularly and in a dynamic fasion
  (e.g. dependent on the axis range), it is a sign that you should probably
  create an own ticker by subclassing BaseChartAxisTicker, instead of using this
  one.

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickertext-creation
*/

/* start of documentation of inline functions */

/*! \fn QMap<double, QString> &BaseChartAxisTickerText::ticks()

  Returns a non-const reference to the internal map which stores the tick
  coordinates and their labels.

  You can access the map directly in order to add, remove or manipulate ticks,
  as an alternative to using the methods provided by BaseChartAxisTickerText,
  such as \ref setTicks and \ref addTick.
*/

/* end of documentation of inline functions */

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerText::BaseChartAxisTickerText() : mSubTickCount(0) {}

/*! \overload

  Sets the ticks that shall appear on the axis. The map key of \a ticks
  corresponds to the axis coordinate, and the map value is the string that will
  appear as tick label.

  An alternative to manipulate ticks is to directly access the internal storage
  with the \ref ticks getter.

  \see addTicks, addTick, clear
*/
void BaseChartAxisTickerText::setTicks(const QMap<double, QString>& ticks) {
  mTicks = ticks;
}

/*! \overload

  Sets the ticks that shall appear on the axis. The entries of \a positions
  correspond to the axis coordinates, and the entries of \a labels are the
  respective strings that will appear as tick labels.

  \see addTicks, addTick, clear
*/
void BaseChartAxisTickerText::setTicks(const QVector<double>& positions,
                                       const QVector<QString>& labels) {
  clear();
  addTicks(positions, labels);
}

/*!
  Sets the number of sub ticks that shall appear between ticks. For
  BaseChartAxisTickerText, there is no automatic sub tick count calculation. So
  if sub ticks are needed, they must be configured with this method.
*/
void BaseChartAxisTickerText::setSubTickCount(int subTicks) {
  if (subTicks >= 0)
    mSubTickCount = subTicks;
  else
    qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
}

/*!
  Clears all ticks.

  An alternative to manipulate ticks is to directly access the internal storage
  with the \ref ticks getter.

  \see setTicks, addTicks, addTick
*/
void BaseChartAxisTickerText::clear() { mTicks.clear(); }

/*!
  Adds a single tick to the axis at the given axis coordinate \a position, with
  the provided tick \a label.

  \see addTicks, setTicks, clear
*/
void BaseChartAxisTickerText::addTick(double position, const QString& label) {
  mTicks.insert(position, label);
}

/*! \overload

  Adds the provided \a ticks to the ones already existing. The map key of \a
  ticks corresponds to the axis coordinate, and the map value is the string that
  will appear as tick label.

  An alternative to manipulate ticks is to directly access the internal storage
  with the \ref ticks getter.

  \see addTick, setTicks, clear
*/
void BaseChartAxisTickerText::addTicks(const QMap<double, QString>& ticks) {
  mTicks.insert(ticks);
}

/*! \overload

  Adds the provided ticks to the ones already existing. The entries of \a
  positions correspond to the axis coordinates, and the entries of \a labels are
  the respective strings that will appear as tick labels.

  An alternative to manipulate ticks is to directly access the internal storage
  with the \ref ticks getter.

  \see addTick, setTicks, clear
*/
void BaseChartAxisTickerText::addTicks(const QVector<double>& positions,
                                       const QVector<QString>& labels) {
  if (positions.size() != labels.size())
    qDebug() << Q_FUNC_INFO
             << "passed unequal length vectors for positions and labels:"
             << positions.size() << labels.size();
  int n = qMin(positions.size(), labels.size());
  for (int i = 0; i < n; ++i) mTicks.insert(positions.at(i), labels.at(i));
}

/*!
  Since the tick coordinates are provided externally, this method implementation
  does nothing.

  \seebaseclassmethod
*/
double BaseChartAxisTickerText::getTickStep(const BaseChartRange& range) {
  // text axis ticker has manual tick positions, so doesn't need this method
  Q_UNUSED(range)
  return 1.0;
}

/*!
  Returns the sub tick count that was configured with \ref setSubTickCount.

  \seebaseclassmethod
*/
int BaseChartAxisTickerText::getSubTickCount(double tickStep) {
  Q_UNUSED(tickStep)
  return mSubTickCount;
}

/*!
  Returns the tick label which corresponds to the key \a tick in the internal
  tick storage. Since the labels are provided externally, \a locale, \a
  formatChar, and \a precision are ignored.

  \seebaseclassmethod
*/
QString BaseChartAxisTickerText::getTickLabel(double tick,
                                              const QLocale& locale,
                                              QChar formatChar, int precision) {
  Q_UNUSED(locale)
  Q_UNUSED(formatChar)
  Q_UNUSED(precision)
  return mTicks.value(tick);
}

/*!
  Returns the externally provided tick coordinates which are in the specified \a
  range. If available, one tick above and below the range is provided in
  addition, to allow possible sub tick calculation. The parameter \a tickStep is
  ignored.

  \seebaseclassmethod
*/
QVector<double> BaseChartAxisTickerText::createTickVector(
    double tickStep, const BaseChartRange& range) {
  Q_UNUSED(tickStep)
  QVector<double> result;
  if (mTicks.isEmpty()) return result;

  QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);
  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
  // this method should try to give one tick outside of range so proper subticks
  // can be generated:
  if (start != mTicks.constBegin()) --start;
  if (end != mTicks.constEnd()) ++end;
  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
    result.append(it.key());

  return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerPi
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerPi
  \brief Specialized axis ticker to display ticks in units of an arbitrary
  constant, for example pi

  \image html axisticker-pi.png

  This BaseChartAxisTicker subclass generates ticks that are expressed with
  respect to a given symbolic constant with a numerical value specified with
  \ref setPiValue and an appearance in the tick labels specified with \ref
  setPiSymbol.

  Ticks may be generated at fractions of the symbolic constant. How these
  fractions appear in the tick label can be configured with \ref
  setFractionStyle.

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickerpi-creation
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerPi::BaseChartAxisTickerPi()
    : mPiSymbol(QLatin1String(" ") + QChar(0x03C0)),
      mPiValue(M_PI),
      mPeriodicity(0),
      mFractionStyle(fsUnicodeFractions),
      mPiTickStep(0) {
  setTickCount(4);
}

/*!
  Sets how the symbol part (which is always a suffix to the number) shall appear
  in the axis tick label.

  If a space shall appear between the number and the symbol, make sure the space
  is contained in \a symbol.
*/
void BaseChartAxisTickerPi::setPiSymbol(QString symbol) { mPiSymbol = symbol; }

/*!
  Sets the numerical value that the symbolic constant has.

  This will be used to place the appropriate fractions of the symbol at the
  respective axis coordinates.
*/
void BaseChartAxisTickerPi::setPiValue(double pi) { mPiValue = pi; }

/*!
  Sets whether the axis labels shall appear periodicly and if so, at which
  multiplicity of the symbolic constant.

  To disable periodicity, set \a multiplesOfPi to zero.

  For example, an axis that identifies 0 with 2pi would set \a multiplesOfPi to
  two.
*/
void BaseChartAxisTickerPi::setPeriodicity(int multiplesOfPi) {
  mPeriodicity = qAbs(multiplesOfPi);
}

/*!
  Sets how the numerical/fractional part preceding the symbolic constant is
  displayed in tick labels. See \ref FractionStyle for the various options.
*/
void BaseChartAxisTickerPi::setFractionStyle(
    BaseChartAxisTickerPi::FractionStyle style) {
  mFractionStyle = style;
}

/*! \internal

  Returns the tick step, using the constant's value (\ref setPiValue) as base
  unit. In consequence the numerical/fractional part preceding the symbolic
  constant is made to have a readable mantissa.

  \seebaseclassmethod
*/
double BaseChartAxisTickerPi::getTickStep(const BaseChartRange& range) {
  mPiTickStep =
      range.size() / mPiValue /
      double(mTickCount +
             1e-10);  // mTickCount ticks on average, the small addition is to
                      // prevent jitter on exact integers
  mPiTickStep = cleanMantissa(mPiTickStep);
  return mPiTickStep * mPiValue;
}

/*! \internal

  Returns the sub tick count, using the constant's value (\ref setPiValue) as
  base unit. In consequence the sub ticks divide the numerical/fractional part
  preceding the symbolic constant reasonably, and not the total tick coordinate.

  \seebaseclassmethod
*/
int BaseChartAxisTickerPi::getSubTickCount(double tickStep) {
  return BaseChartAxisTicker::getSubTickCount(tickStep / mPiValue);
}

/*! \internal

  Returns the tick label as a fractional/numerical part and a symbolic string as
  suffix. The formatting of the fraction is done according to the specified \ref
  setFractionStyle. The appended symbol is specified with \ref setPiSymbol.

  \seebaseclassmethod
*/
QString BaseChartAxisTickerPi::getTickLabel(double tick, const QLocale& locale,
                                            QChar formatChar, int precision) {
  double tickInPis = tick / mPiValue;
  if (mPeriodicity > 0) tickInPis = fmod(tickInPis, mPeriodicity);

  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 &&
      mPiTickStep < 50) {
    // simply construct fraction from decimal like 1.234 -> 1234/1000 and then
    // simplify fraction, smaller digits are irrelevant due to mPiTickStep
    // conditional above
    int denominator = 1000;
    int numerator = qRound(tickInPis * denominator);
    simplifyFraction(numerator, denominator);
    if (qAbs(numerator) == 1 && denominator == 1)
      return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) +
             mPiSymbol.trimmed();
    else if (numerator == 0)
      return QLatin1String("0");
    else
      return fractionToString(numerator, denominator) + mPiSymbol;
  } else {
    if (qFuzzyIsNull(tickInPis))
      return QLatin1String("0");
    else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
      return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) +
             mPiSymbol.trimmed();
    else
      return BaseChartAxisTicker::getTickLabel(tickInPis, locale, formatChar,
                                               precision) +
             mPiSymbol;
  }
}

/*! \internal

  Takes the fraction given by \a numerator and \a denominator and modifies the
  values to make sure the fraction is in irreducible form, i.e. numerator and
  denominator don't share any common factors which could be cancelled.
*/
void BaseChartAxisTickerPi::simplifyFraction(int& numerator,
                                             int& denominator) const {
  if (numerator == 0 || denominator == 0) return;

  int num = numerator;
  int denom = denominator;
  while (denom != 0)  // euclidean gcd algorithm
  {
    int oldDenom = denom;
    denom = num % denom;
    num = oldDenom;
  }
  // num is now gcd of numerator and denominator
  numerator /= num;
  denominator /= num;
}

/*! \internal

  Takes the fraction given by \a numerator and \a denominator and returns a
  string representation. The result depends on the configured fraction style
  (\ref setFractionStyle).

  This method is used to format the numerical/fractional part when generating
  tick labels. It simplifies the passed fraction to an irreducible form using
  \ref simplifyFraction and factors out any integer parts of the fraction (e.g.
  "10/4" becomes "2 1/2").
*/
QString BaseChartAxisTickerPi::fractionToString(int numerator,
                                                int denominator) const {
  if (denominator == 0) {
    qDebug() << Q_FUNC_INFO << "called with zero denominator";
    return QString();
  }
  if (mFractionStyle ==
      fsFloatingPoint)  // should never be the case when calling this function
  {
    qDebug() << Q_FUNC_INFO
             << "shouldn't be called with fraction style fsDecimal";
    return QString::number(numerator / double(denominator));  // failsafe
  }
  int sign = numerator * denominator < 0 ? -1 : 1;
  numerator = qAbs(numerator);
  denominator = qAbs(denominator);

  if (denominator == 1) {
    return QString::number(sign * numerator);
  } else {
    int integerPart = numerator / denominator;
    int remainder = numerator % denominator;
    if (remainder == 0) {
      return QString::number(sign * integerPart);
    } else {
      if (mFractionStyle == fsAsciiFractions) {
        return QString(QLatin1String("%1%2%3/%4"))
            .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
            .arg(integerPart > 0
                     ? QString::number(integerPart) + QLatin1String(" ")
                     : QString(QLatin1String("")))
            .arg(remainder)
            .arg(denominator);
      } else if (mFractionStyle == fsUnicodeFractions) {
        return QString(QLatin1String("%1%2%3"))
            .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
            .arg(integerPart > 0 ? QString::number(integerPart)
                                 : QLatin1String(""))
            .arg(unicodeFraction(remainder, denominator));
      }
    }
  }
  return QString();
}

/*! \internal

  Returns the unicode string representation of the fraction given by \a
  numerator and \a denominator. This is the representation used in \ref
  fractionToString when the fraction style
  (\ref setFractionStyle) is \ref fsUnicodeFractions.

  This method doesn't use the single-character common fractions but builds each
  fraction from a superscript unicode number, the unicode fraction character,
  and a subscript unicode number.
*/
QString BaseChartAxisTickerPi::unicodeFraction(int numerator,
                                               int denominator) const {
  return unicodeSuperscript(numerator) + QChar(0x2044) +
         unicodeSubscript(denominator);
}

/*! \internal

  Returns the unicode string representing \a number as superscript. This is used
  to build unicode fractions in \ref unicodeFraction.
*/
QString BaseChartAxisTickerPi::unicodeSuperscript(int number) const {
  if (number == 0) return QString(QChar(0x2070));

  QString result;
  while (number > 0) {
    const int digit = number % 10;
    switch (digit) {
      case 1: {
        result.prepend(QChar(0x00B9));
        break;
      }
      case 2: {
        result.prepend(QChar(0x00B2));
        break;
      }
      case 3: {
        result.prepend(QChar(0x00B3));
        break;
      }
      default: {
        result.prepend(QChar(0x2070 + digit));
        break;
      }
    }
    number /= 10;
  }
  return result;
}

/*! \internal

  Returns the unicode string representing \a number as subscript. This is used
  to build unicode fractions in \ref unicodeFraction.
*/
QString BaseChartAxisTickerPi::unicodeSubscript(int number) const {
  if (number == 0) return QString(QChar(0x2080));

  QString result;
  while (number > 0) {
    result.prepend(QChar(0x2080 + number % 10));
    number /= 10;
  }
  return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisTickerLog
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartAxisTickerLog
  \brief Specialized axis ticker suited for logarithmic axes

  \image html axisticker-log.png

  This BaseChartAxisTicker subclass generates ticks with unequal tick intervals
  suited for logarithmic axis scales. The ticks are placed at powers of the
  specified log base (\ref setLogBase).

  Especially in the case of a log base equal to 10 (the default), it might be
  desirable to have tick labels in the form of powers of ten without mantissa
  display. To achieve this, set the number precision (\ref
  BaseChartAxis::setNumberPrecision) to zero and the number format (\ref
  BaseChartAxis::setNumberFormat) to scientific (exponential) display with
  beautifully typeset decimal powers, so a format string of <tt>"eb"</tt>. This
  will result in the following axis tick labels:

  \image html axisticker-log-powers.png

  The ticker can be created and assigned to an axis like this:
  \snippet documentation/doc-image-generator/mainwindow.cpp
  axistickerlog-creation

  Note that the nature of logarithmic ticks imply that there exists a smallest
  possible tick step, corresponding to one multiplication by the log base. If
  the user zooms in further than that, no new ticks would appear, leading to
  very sparse or even no axis ticks on the axis. To prevent this situation, this
  ticker falls back to regular tick generation if the axis range would be
  covered by too few logarithmically placed ticks.
*/

/*!
  Constructs the ticker and sets reasonable default values. Axis tickers are
  commonly created managed by a QSharedPointer, which then can be passed to
  BaseChartAxis::setTicker.
*/
BaseChartAxisTickerLog::BaseChartAxisTickerLog()
    : mLogBase(10.0),
      mSubTickCount(8),  // generates 10 intervals
      mLogBaseLnInv(1.0 / qLn(mLogBase)) {}

/*!
  Sets the logarithm base used for tick coordinate generation. The ticks will be
  placed at integer powers of \a base.
*/
void BaseChartAxisTickerLog::setLogBase(double base) {
  if (base > 0) {
    mLogBase = base;
    mLogBaseLnInv = 1.0 / qLn(mLogBase);
  } else
    qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base;
}

/*!
  Sets the number of sub ticks in a tick interval. Within each interval, the sub
  ticks are spaced linearly to provide a better visual guide, so the sub tick
  density increases toward the higher tick.

  Note that \a subTicks is the number of sub ticks (not sub intervals) in one
  tick interval. So in the case of logarithm base 10 an intuitive sub tick
  spacing would be achieved with eight sub ticks (the default). This means e.g.
  between the ticks 10 and 100 there will be eight ticks, namely at 20, 30, 40,
  50, 60, 70, 80 and 90.
*/
void BaseChartAxisTickerLog::setSubTickCount(int subTicks) {
  if (subTicks >= 0)
    mSubTickCount = subTicks;
  else
    qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
}

/*! \internal

  Returns the sub tick count specified in \ref setSubTickCount. For
  BaseChartAxisTickerLog, there is no automatic sub tick count calculation
  necessary.

  \seebaseclassmethod
*/
int BaseChartAxisTickerLog::getSubTickCount(double tickStep) {
  Q_UNUSED(tickStep)
  return mSubTickCount;
}

/*! \internal

  Creates ticks with a spacing given by the logarithm base and an increasing
  integer power in the provided \a range. The step in which the power increases
  tick by tick is chosen in order to keep the total number of ticks as close as
  possible to the tick count (\ref setTickCount).

  The parameter \a tickStep is ignored for the normal logarithmic ticker
  generation. Only when zoomed in very far such that not enough logarithmically
  placed ticks would be visible, this function falls back to the regular
  BaseChartAxisTicker::createTickVector, which then uses \a tickStep.

  \seebaseclassmethod
*/
QVector<double> BaseChartAxisTickerLog::createTickVector(
    double tickStep, const BaseChartRange& range) {
  QVector<double> result;
  if (range.lower > 0 && range.upper > 0)  // positive range
  {
    const double baseTickCount = qLn(range.upper / range.lower) * mLogBaseLnInv;
    if (baseTickCount <
        1.6)  // if too few log ticks would be visible in axis range, fall back
              // to regular tick vector generation
      return BaseChartAxisTicker::createTickVector(tickStep, range);
    const double exactPowerStep = baseTickCount / double(mTickCount + 1e-10);
    const double newLogBase =
        qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
    double currentTick =
        qPow(newLogBase, qFloor(qLn(range.lower) / qLn(newLogBase)));
    result.append(currentTick);
    while (currentTick < range.upper &&
           currentTick > 0)  // currentMag might be zero for ranges ~1e-300,
                             // just cancel in that case
    {
      currentTick *= newLogBase;
      result.append(currentTick);
    }
  } else if (range.lower < 0 && range.upper < 0)  // negative range
  {
    const double baseTickCount = qLn(range.lower / range.upper) * mLogBaseLnInv;
    if (baseTickCount <
        1.6)  // if too few log ticks would be visible in axis range, fall back
              // to regular tick vector generation
      return BaseChartAxisTicker::createTickVector(tickStep, range);
    const double exactPowerStep = baseTickCount / double(mTickCount + 1e-10);
    const double newLogBase =
        qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
    double currentTick =
        -qPow(newLogBase, qCeil(qLn(-range.lower) / qLn(newLogBase)));
    result.append(currentTick);
    while (currentTick < range.upper &&
           currentTick < 0)  // currentMag might be zero for ranges ~1e-300,
                             // just cancel in that case
    {
      currentTick /= newLogBase;
      result.append(currentTick);
    }
  } else  // invalid range for logarithmic scale, because lower and upper have
          // different sign
  {
    qDebug() << Q_FUNC_INFO
             << "Invalid range for logarithmic plot: " << range.lower << ".."
             << range.upper;
  }

  return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// QBAxisGrid
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class QBAxisGrid
  \brief Responsible for drawing the grid of a BaseChartAxis.

  This class is tightly bound to BaseChartAxis. Every axis owns a grid instance
  and uses it to draw the grid lines, sub grid lines and zero-line. You can
  interact with the grid of an axis via \ref BaseChartAxis::grid. Normally, you
  don't need to create an instance of QBAxisGrid yourself.

  The axis and grid drawing was split into two classes to allow them to be
  placed on different layers (both BaseChartAxis and QBAxisGrid inherit from
  BaseChartLayerable). Thus it is possible to have the grid in the background
  and the axes in the foreground, and any plottables/items in between. This
  described situation is the default setup, see the QCPLayer documentation.
*/

/*!
  Creates a QBAxisGrid instance and sets default values.

  You shouldn't instantiate grids on their own, since every BaseChartAxis brings
  its own QBAxisGrid.
*/
QBAxisGrid::QBAxisGrid(BaseChartAxis* parentAxis)
    : BaseChartLayerable(parentAxis->parentPlot(), QString(), parentAxis),
      mSubGridVisible{},
      mAntialiasedSubGrid{},
      mAntialiasedZeroLine{},
      mParentAxis(parentAxis),
      mCustomAxisGrid(nullptr) {
  // warning: this is called in BaseChartAxis constructor, so parentAxis members
  // should not be accessed/called
  setParent(parentAxis);
  setPen(QPen(QColor(37, 51, 48), 0, Qt::SolidLine));
  setSubGridPen(QPen(QColor(37, 51, 48), 0, Qt::SolidLine));
  setZeroLinePen(QPen(QColor(37, 51, 48), 0, Qt::SolidLine));
  setSubGridVisible(false);
  setAntialiased(false);
  setAntialiasedSubGrid(false);
  setAntialiasedZeroLine(false);
}

/*!
  Sets whether grid lines at sub tick marks are drawn.

  \see setSubGridPen
*/
void QBAxisGrid::setSubGridVisible(bool visible) { mSubGridVisible = visible; }

/*!
  Sets whether sub grid lines are drawn antialiased.
*/
void QBAxisGrid::setAntialiasedSubGrid(bool enabled) {
  mAntialiasedSubGrid = enabled;
}

/*!
  Sets whether zero lines are drawn antialiased.
*/
void QBAxisGrid::setAntialiasedZeroLine(bool enabled) {
  mAntialiasedZeroLine = enabled;
}

/*!
  Sets the pen with which (major) grid lines are drawn.
*/
void QBAxisGrid::setPen(const QPen& pen) { mPen = pen; }

/*!
  Sets the pen with which sub grid lines are drawn.
*/
void QBAxisGrid::setSubGridPen(const QPen& pen) { mSubGridPen = pen; }

/*!
  Sets the pen with which zero lines are drawn.

  Zero lines are lines at value coordinate 0 which may be drawn with a different
  pen than other grid lines. To disable zero lines and just draw normal grid
  lines at zero, set \a pen to Qt::NoPen.
*/
void QBAxisGrid::setZeroLinePen(const QPen& pen) { mZeroLinePen = pen; }

void QBAxisGrid::setCustomAxisGrid(BaseChartCustomAxisGrid* grid) {
  mCustomAxisGrid = grid;
}

/*! \internal

  A convenience function to easily set the QPainter::Antialiased hint on the
  provided \a painter before drawing the major grid lines.

  This is the antialiasing state the painter passed to the \ref draw method is
  in by default.

  This function takes into account the local setting of the antialiasing flag as
  well as the overrides set with \ref
  BaseChartCustomPlot::setAntialiasedElements and \ref
  BaseChartCustomPlot::setNotAntialiasedElements.

  \see setAntialiased
*/
void QBAxisGrid::applyDefaultAntialiasingHint(BaseChartPainter* painter) const {
  applyAntialiasingHint(painter, mAntialiased, aeGrid);
}

/*! \internal

  Draws grid lines and sub grid lines at the positions of (sub) ticks of the
  parent axis, spanning over the complete axis rect. Also draws the zero line,
  if appropriate (\ref setZeroLinePen).
*/
void QBAxisGrid::draw(BaseChartPainter* painter) {
  if (!mParentAxis) {
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
    return;
  }

  if (mParentAxis->subTicks() && mSubGridVisible) drawSubGridLines(painter);
  drawGridLines(painter);
}

/*! \internal

  Draws the main grid lines and possibly a zero line with the specified painter.

  This is a helper function called by \ref draw.
*/
void QBAxisGrid::drawGridLines(BaseChartPainter* painter) const {
  if (!mParentAxis) {
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
    return;
  }

  const int tickCount = mParentAxis->mTickVector.size();
  double t;  // helper variable, result of coordinate-to-pixel transforms
  if (mParentAxis->orientation() == Qt::Horizontal) {
    applyDefaultAntialiasingHint(painter);
    painter->setPen(mPen);
    if (mCustomAxisGrid && mCustomAxisGrid->drawGridXLines(painter)) {
      return;
    }

    // draw zeroline:
    int zeroLineIndex = -1;
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
        mParentAxis->mRange.upper > 0) {
      applyAntialiasingHint(painter, mAntialiasedZeroLine, aeZeroLine);
      painter->setPen(mZeroLinePen);
      double epsilon =
          mParentAxis->range().size() * 1E-6;  // for comparing double to zero
      for (int i = 0; i < tickCount; ++i) {
        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
          zeroLineIndex = i;
          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
          painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
                                   mParentAxis->mAxisRect->top()));
          break;
        }
      }
    }
    // draw grid lines:

    int top = 0, bottom = 0;
    if (mParentAxis->mYAxis) {
      top = mParentAxis->mYAxis->upperRangePixel();
      bottom = mParentAxis->mYAxis->lowerRangePixel();
    } else {
      top = mParentAxis->mAxisRect->top();
      bottom = mParentAxis->mAxisRect->bottom();
    }

    applyDefaultAntialiasingHint(painter);
    painter->setPen(mPen);
    for (int i = 0; i < tickCount; ++i) {
      if (i == zeroLineIndex)
        continue;  // don't draw a gridline on top of the zeroline
      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
      painter->drawLine(QLineF(t, bottom, t, top));
    }
  } else {
    applyDefaultAntialiasingHint(painter);
    painter->setPen(mPen);
    if (mCustomAxisGrid && mCustomAxisGrid->drawGridYLines(painter)) {
      return;
    }

    // draw zeroline:
    int zeroLineIndex = -1;
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
        mParentAxis->mRange.upper > 0) {
      applyAntialiasingHint(painter, mAntialiasedZeroLine, aeZeroLine);
      painter->setPen(mZeroLinePen);
      double epsilon =
          mParentAxis->mRange.size() * 1E-6;  // for comparing double to zero
      for (int i = 0; i < tickCount; ++i) {
        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
          zeroLineIndex = i;
          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // y
          painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
                                   mParentAxis->mAxisRect->right(), t));
          break;
        }
      }
    }
    // draw grid lines:
    applyDefaultAntialiasingHint(painter);
    painter->setPen(mPen);
    for (int i = 0; i < tickCount; ++i) {
      if (i == zeroLineIndex)
        continue;  // don't draw a gridline on top of the zeroline
      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // y
      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
                               mParentAxis->mAxisRect->right(), t));
    }
  }
}

/*! \internal

  Draws the sub grid lines with the specified painter.

  This is a helper function called by \ref draw.
*/
void QBAxisGrid::drawSubGridLines(BaseChartPainter* painter) const {
  if (!mParentAxis) {
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
    return;
  }

  applyAntialiasingHint(painter, mAntialiasedSubGrid, aeSubGrid);
  double t;  // helper variable, result of coordinate-to-pixel transforms
  painter->setPen(mSubGridPen);
  if (mParentAxis->orientation() == Qt::Horizontal) {
    foreach (double tickCoord, mParentAxis->mSubTickVector) {
      t = mParentAxis->coordToPixel(tickCoord);  // x
      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
                               mParentAxis->mAxisRect->top()));
    }
  } else {
    foreach (double tickCoord, mParentAxis->mSubTickVector) {
      t = mParentAxis->coordToPixel(tickCoord);  // y
      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
                               mParentAxis->mAxisRect->right(), t));
    }
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxis
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartAxis
  \brief Manages a single axis inside a BaseChartCustomPlot.

  Usually doesn't need to be instantiated externally. Access
  %BaseChartCustomPlot's default four axes via BaseChartCustomPlot::xAxis
  (bottom), BaseChartCustomPlot::yAxis (left), BaseChartCustomPlot::xAxis2 (top)
  and BaseChartCustomPlot::yAxis2 (right).

  Axes are always part of an axis rect, see BaseChartAxisRect.
  \image html AxisNamesOverview.png
  <center>Naming convention of axis parts</center>
  \n

  \image html AxisRectSpacingOverview.png
  <center>Overview of the spacings and paddings that define the geometry of an
  axis. The dashed gray line on the left represents the BaseChartCustomPlot
  widget border.</center>

  Each axis holds an instance of BaseChartAxisTicker which is used to generate
  the tick coordinates and tick labels. You can access the currently installed
  \ref ticker or set a new one (possibly one of the specialized subclasses, or
  your own subclass) via \ref setTicker. For details, see the documentation of
  BaseChartAxisTicker.
*/

/* start of documentation of inline functions */

/*! \fn Qt::Orientation BaseChartAxis::orientation() const

  Returns the orientation of this axis. The axis orientation (horizontal or
  vertical) is deduced from the axis type (left, top, right or bottom).

  \see orientation(AxisType type), pixelOrientation
*/

/*! \fn QBAxisGrid *BaseChartAxis::grid() const

  Returns the \ref QBAxisGrid instance belonging to this axis. Access it to set
  details about the way the grid is displayed.
*/

/*! \fn static Qt::Orientation BaseChartAxis::orientation(AxisType type)

  Returns the orientation of the specified axis type

  \see orientation(), pixelOrientation
*/

/*! \fn int BaseChartAxis::pixelOrientation() const

  Returns which direction points towards higher coordinate values/keys, in pixel
  space.

  This method returns either 1 or -1. If it returns 1, then going in the
  positive direction along the orientation of the axis in pixels corresponds to
  going from lower to higher axis coordinates. On the other hand, if this method
  returns -1, going to smaller pixel values corresponds to going from lower to
  higher axis coordinates.

  For example, this is useful to easily shift axis coordinates by a certain
  amount given in pixels, without having to care about reversed or vertically
  aligned axes:

  \code
  double newKey =
  keyAxis->pixelToCoord(keyAxis->coordToPixel(oldKey)+10*keyAxis->pixelOrientation());
  \endcode

  \a newKey will then contain a key that is ten pixels towards higher keys,
  starting from \a oldKey.
*/

/*! \fn QSharedPointer<BaseChartAxisTicker> BaseChartAxis::ticker() const

  Returns a modifiable shared pointer to the currently installed axis ticker.
  The axis ticker is responsible for generating the tick positions and tick
  labels of this axis. You can access the \ref BaseChartAxisTicker with this
  method and modify basic properties such as the approximate tick count
  (\ref BaseChartAxisTicker::setTickCount).

  You can gain more control over the axis ticks by setting a different \ref
  BaseChartAxisTicker subclass, see the documentation there. A new axis ticker
  can be set with \ref setTicker.

  Since the ticker is stored in the axis as a shared pointer, multiple axes may
  share the same axis ticker simply by passing the same shared pointer to
  multiple axes.

  \see setTicker
*/

/* end of documentation of inline functions */
/* start of documentation of signals */

/*! \fn void BaseChartAxis::rangeChanged(const BaseChartRange &newRange)

  This signal is emitted when the range of this axis has changed. You can
  connect it to the \ref setRange slot of another axis to communicate the new
  range to the other axis, in order for it to be synchronized.

  You may also manipulate/correct the range with \ref setRange in a slot
  connected to this signal. This is useful if for example a maximum range span
  shall not be exceeded, or if the lower/upper range shouldn't go beyond certain
  values (see \ref BaseChartRange::bounded). For example, the following slot
  would limit the x axis to ranges between 0 and 10: \code
  customPlot->xAxis->setRange(newRange.bounded(0, 10))
  \endcode
*/

/*! \fn void BaseChartAxis::rangeChanged(const BaseChartRange &newRange, const
  BaseChartRange &oldRange) \overload

  Additionally to the new range, this signal also provides the previous range
  held by the axis as \a oldRange.
*/

/*! \fn void BaseChartAxis::scaleTypeChanged(BaseChartAxis::ScaleType
  scaleType);

  This signal is emitted when the scale type changes, by calls to \ref
  setScaleType
*/

/*! \fn void BaseChartAxis::selectionChanged(BaseChartAxis::SelectableParts
  selection)

  This signal is emitted when the selection state of this axis has changed,
  either by user interaction or by a direct call to \ref setSelectedParts.
*/

/*! \fn void BaseChartAxis::selectableChanged(const
  BaseChartAxis::SelectableParts &parts);

  This signal is emitted when the selectability changes, by calls to \ref
  setSelectableParts
*/

/* end of documentation of signals */

/*!
  Constructs an Axis instance of Type \a type for the axis rect \a parent.

  Usually it isn't necessary to instantiate axes directly, because you can let
  BaseChartCustomPlot create them for you with \ref BaseChartAxisRect::addAxis.
  If you want to use own BaseChartAxis-subclasses however, create them manually
  and then inject them also via \ref BaseChartAxisRect::addAxis.
*/
BaseChartAxis::BaseChartAxis(BaseChartAxisRect* parent, AxisType type)
    : BaseChartLayerable(parent->parentPlot(), QString(), parent),
      // axis base:
      mAxisType(type),
      mAxisRect(parent),
      mPadding(5),
      mOrientation(orientation(type)),
      mSelectableParts(spAxis | spTickLabels | spAxisLabel),
      mSelectedParts(spNone),
      mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      mSelectedBasePen(QPen(Qt::blue, 2)),
      // axis label:
      mLabel(),
      mLabelFont(mParentPlot->font()),
      mSelectedLabelFont(
          QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
      mLabelColor(Qt::black),
      mSelectedLabelColor(Qt::blue),
      // tick labels:
      mTickLabels(true),
      mTickLabelFont(mParentPlot->font()),
      mSelectedTickLabelFont(QFont(mTickLabelFont.family(),
                                   mTickLabelFont.pointSize(), QFont::Bold)),
      mTickLabelColor(Qt::darkGray),
      mSelectedTickLabelColor(Qt::blue),
      mNumberPrecision(4),
      mNumberFormatChar('g'),
      mNumberBeautifulPowers(true),
      // ticks and subticks:
      mTicks(true),
      mSubTicks(true),
      mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      mSelectedTickPen(QPen(Qt::blue, 2)),
      mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      mSelectedSubTickPen(QPen(Qt::blue, 2)),
      // scale and range:
      mRange(0, 5),
      mRangeReversed(false),
      mScaleType(stLinear),
      // internal members:
      mYAxis(nullptr),
      mGrid(new QBAxisGrid(this)),
      mAxisPainter(new BaseChartAxisPainterPrivate(parent->parentPlot())),
      mTicker(new BaseChartAxisTicker),
      mCachedMarginValid(false),
      mCachedMargin(0),
      mDragging(false) {
  setParent(parent);
  mGrid->setVisible(false);
  setAntialiased(false);
  setLayer(
      mParentPlot->currentLayer());  // it's actually on that layer already, but
                                     // we want it in front of the grid, so we
                                     // place it on there again

  if (type == atTop) {
    setTickLabelPadding(3);
    setLabelPadding(6);
  } else if (type == atRight) {
    setTickLabelPadding(7);
    setLabelPadding(12);
  } else if (type == atBottom) {
    setTickLabelPadding(3);
    setLabelPadding(3);
  } else if (type == atLeft) {
    setTickLabelPadding(5);
    setLabelPadding(10);
  }
}

BaseChartAxis::~BaseChartAxis() {
  delete mAxisPainter;
  delete mGrid;  // delete grid here instead of via parent ~QObject for better
                 // defined deletion order
}

double BaseChartAxis::lowerRangePixel() const {
  double lower = 999999;
  for (int i = 0; i < mTickVector.size(); ++i) {
    double temp = coordToPixel(mTickVector.at(i));
    if (lower > temp) {
      lower = temp;
    }
  }
  return lower;
}

double BaseChartAxis::upperRangePixel() const {
  double upper = -999999;
  for (int i = 0; i < mTickVector.size(); ++i) {
    double temp = coordToPixel(mTickVector.at(i));
    if (temp > upper) {
      upper = temp;
    }
  }
  return upper;
}

double BaseChartAxis::lowerCoord() const {
  double lower = 999999;
  for (int i = 0; i < mTickVector.size(); ++i) {
    double temp = mTickVector.at(i);
    if (lower > temp) {
      lower = temp;
    }
  }
  return lower;
}

double BaseChartAxis::upperCoord() const {
  double upper = -999999;
  for (int i = 0; i < mTickVector.size(); ++i) {
    double temp = mTickVector.at(i);
    if (temp > upper) {
      upper = temp;
    }
  }
  return upper;
}

/* No documentation as it is a property getter */
int BaseChartAxis::tickLabelPadding() const {
  return mAxisPainter->tickLabelPadding;
}

/* No documentation as it is a property getter */
double BaseChartAxis::tickLabelRotation() const {
  return mAxisPainter->tickLabelRotation;
}

/* No documentation as it is a property getter */
BaseChartAxis::LabelSide BaseChartAxis::tickLabelSide() const {
  return mAxisPainter->tickLabelSide;
}

/* No documentation as it is a property getter */
QString BaseChartAxis::numberFormat() const {
  QString result;
  result.append(mNumberFormatChar);
  if (mNumberBeautifulPowers) {
    result.append(QLatin1Char('b'));
    if (mAxisPainter->numberMultiplyCross) result.append(QLatin1Char('c'));
  }
  return result;
}

/* No documentation as it is a property getter */
int BaseChartAxis::tickLengthIn() const { return mAxisPainter->tickLengthIn; }

/* No documentation as it is a property getter */
int BaseChartAxis::tickLengthOut() const { return mAxisPainter->tickLengthOut; }

/* No documentation as it is a property getter */
int BaseChartAxis::subTickLengthIn() const {
  return mAxisPainter->subTickLengthIn;
}

/* No documentation as it is a property getter */
int BaseChartAxis::subTickLengthOut() const {
  return mAxisPainter->subTickLengthOut;
}

/* No documentation as it is a property getter */
int BaseChartAxis::labelPadding() const { return mAxisPainter->labelPadding; }

/* No documentation as it is a property getter */
int BaseChartAxis::offset() const { return mAxisPainter->offset; }

/* No documentation as it is a property getter */
BaseChartLineEnding BaseChartAxis::lowerEnding() const {
  return mAxisPainter->lowerEnding;
}

/* No documentation as it is a property getter */
BaseChartLineEnding BaseChartAxis::upperEnding() const {
  return mAxisPainter->upperEnding;
}

/*!
  Sets whether the axis uses a linear scale or a logarithmic scale.

  Note that this method controls the coordinate transformation. For logarithmic
  scales, you will likely also want to use a logarithmic tick spacing and
  labeling, which can be achieved by setting the axis ticker to an instance of
  \ref BaseChartAxisTickerLog :

  \snippet documentation/doc-code-snippets/mainwindow.cpp
  qcpaxisticker-log-creation

  See the documentation of \ref BaseChartAxisTickerLog about the details of
  logarithmic axis tick creation.

  \ref setNumberPrecision
*/
void BaseChartAxis::setScaleType(BaseChartAxis::ScaleType type) {
  if (mScaleType != type) {
    mScaleType = type;
    if (mScaleType == stLogarithmic) setRange(mRange.sanitizedForLogScale());
    mCachedMarginValid = false;
    emit scaleTypeChanged(mScaleType);
  }
}

/*!
  Sets the range of the axis.

  This slot may be connected with the \ref rangeChanged signal of another axis
  so this axis is always synchronized with the other axis range, when it
  changes.

  To invert the direction of an axis, use \ref setRangeReversed.
*/
void BaseChartAxis::setRange(const BaseChartRange& range) {
  if (range.lower == mRange.lower && range.upper == mRange.upper) return;

  if (!BaseChartRange::validRange(range)) return;
  BaseChartRange oldRange = mRange;
  if (mScaleType == stLogarithmic) {
    mRange = range.sanitizedForLogScale();
  } else {
    mRange = range.sanitizedForLinScale();
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  Sets whether the user can (de-)select the parts in \a selectable by clicking
  on the BaseChartCustomPlot surface. (When \ref
  BaseChartCustomPlot::setInteractions contains iSelectAxes.)

  However, even when \a selectable is set to a value not allowing the selection
  of a specific part, it is still possible to set the selection of this part
  manually, by calling \ref setSelectedParts directly.

  \see SelectablePart, setSelectedParts
*/
void BaseChartAxis::setSelectableParts(const SelectableParts& selectable) {
  if (mSelectableParts != selectable) {
    mSelectableParts = selectable;
    emit selectableChanged(mSelectableParts);
  }
}

/*!
  Sets the selected state of the respective axis parts described by \ref
  SelectablePart. When a part is selected, it uses a different pen/font.

  The entire selection mechanism for axes is handled automatically when \ref
  BaseChartCustomPlot::setInteractions contains iSelectAxes. You only need to
  call this function when you wish to change the selection state manually.

  This function can change the selection state of a part, independent of the
  \ref setSelectableParts setting.

  emits the \ref selectionChanged signal when \a selected is different from the
  previous selection state.

  \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen,
  setSelectedTickPen, setSelectedSubTickPen, setSelectedTickLabelFont,
  setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
*/
void BaseChartAxis::setSelectedParts(const SelectableParts& selected) {
  if (mSelectedParts != selected) {
    mSelectedParts = selected;
    emit selectionChanged(mSelectedParts);
  }
}

/*!
  \overload

  Sets the lower and upper bound of the axis range.

  To invert the direction of an axis, use \ref setRangeReversed.

  There is also a slot to set a range, see \ref setRange(const BaseChartRange
  &range).
*/
void BaseChartAxis::setRange(double lower, double upper) {
  if (lower == mRange.lower && upper == mRange.upper) return;

  if (!BaseChartRange::validRange(lower, upper)) return;
  BaseChartRange oldRange = mRange;
  mRange.lower = lower;
  mRange.upper = upper;
  if (mScaleType == stLogarithmic) {
    mRange = mRange.sanitizedForLogScale();
  } else {
    mRange = mRange.sanitizedForLinScale();
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  \overload

  Sets the range of the axis.

  The \a position coordinate indicates together with the \a alignment parameter,
  where the new range will be positioned. \a size defines the size of the new
  axis range. \a alignment may be Qt::AlignLeft, Qt::AlignRight or
  Qt::AlignCenter. This will cause the left border, right border, or center of
  the range to be aligned with \a position. Any other values of \a alignment
  will default to Qt::AlignCenter.
*/
void BaseChartAxis::setRange(double position, double size,
                             Qt::AlignmentFlag alignment) {
  if (alignment == Qt::AlignLeft)
    setRange(position, position + size);
  else if (alignment == Qt::AlignRight)
    setRange(position - size, position);
  else  // alignment == Qt::AlignCenter
    setRange(position - size / 2.0, position + size / 2.0);
}

/*!
  Sets the lower bound of the axis range. The upper bound is not changed.
  \see setRange
*/
void BaseChartAxis::setRangeLower(double lower) {
  if (mRange.lower == lower) return;

  BaseChartRange oldRange = mRange;
  mRange.lower = lower;
  if (mScaleType == stLogarithmic) {
    mRange = mRange.sanitizedForLogScale();
  } else {
    mRange = mRange.sanitizedForLinScale();
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  Sets the upper bound of the axis range. The lower bound is not changed.
  \see setRange
*/
void BaseChartAxis::setRangeUpper(double upper) {
  if (mRange.upper == upper) return;

  BaseChartRange oldRange = mRange;
  mRange.upper = upper;
  if (mScaleType == stLogarithmic) {
    mRange = mRange.sanitizedForLogScale();
  } else {
    mRange = mRange.sanitizedForLinScale();
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  Sets whether the axis range (direction) is displayed reversed. Normally, the
  values on horizontal axes increase left to right, on vertical axes bottom to
  top. When \a reversed is set to true, the direction of increasing values is
  inverted.

  Note that the range and data interface stays the same for reversed axes, e.g.
  the \a lower part of the \ref setRange interface will still reference the
  mathematically smaller number than the \a upper part.
*/
void BaseChartAxis::setRangeReversed(bool reversed) {
  mRangeReversed = reversed;
}

/*!
  The axis ticker is responsible for generating the tick positions and tick
  labels. See the documentation of BaseChartAxisTicker for details on how to
  work with axis tickers.

  You can change the tick positioning/labeling behaviour of this axis by setting
  a different BaseChartAxisTicker subclass using this method. If you only wish
  to modify the currently installed axis ticker, access it via \ref ticker.

  Since the ticker is stored in the axis as a shared pointer, multiple axes may
  share the same axis ticker simply by passing the same shared pointer to
  multiple axes.

  \see ticker
*/
void BaseChartAxis::setTicker(QSharedPointer<BaseChartAxisTicker> ticker) {
  if (ticker)
    mTicker = ticker;
  else
    qDebug() << Q_FUNC_INFO << "can not set nullptr as axis ticker";
  // no need to invalidate margin cache here because produced tick labels are
  // checked for changes in setupTickVector
}

/*!
  Sets whether tick marks are displayed.

  Note that setting \a show to false does not imply that tick labels are
  invisible, too. To achieve that, see \ref setTickLabels.

  \see setSubTicks
*/
void BaseChartAxis::setTicks(bool show) {
  if (mTicks != show) {
    mTicks = show;
    mCachedMarginValid = false;
  }
}

/*!
  Sets whether tick labels are displayed. Tick labels are the numbers drawn next
  to tick marks.
*/
void BaseChartAxis::setTickLabels(bool show) {
  if (mTickLabels != show) {
    mTickLabels = show;
    mCachedMarginValid = false;
    if (!mTickLabels) mTickVectorLabels.clear();
  }
}

/*!
  Sets the distance between the axis base line (including any outward ticks) and
  the tick labels. \see setLabelPadding, setPadding
*/
void BaseChartAxis::setTickLabelPadding(int padding) {
  if (mAxisPainter->tickLabelPadding != padding) {
    mAxisPainter->tickLabelPadding = padding;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the font of the tick labels.

  \see setTickLabels, setTickLabelColor
*/
void BaseChartAxis::setTickLabelFont(const QFont& font) {
  if (font != mTickLabelFont) {
    mTickLabelFont = font;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the color of the tick labels.

  \see setTickLabels, setTickLabelFont
*/
void BaseChartAxis::setTickLabelColor(const QColor& color) {
  mTickLabelColor = color;
}

/*!
  Sets the rotation of the tick labels. If \a degrees is zero, the labels are
  drawn normally. Else, the tick labels are drawn rotated by \a degrees
  clockwise. The specified angle is bound to values from -90 to 90 degrees.

  If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the
  tick coordinate. For other angles, the label is drawn with an offset such that
  it seems to point toward or away from the tick mark.
*/
void BaseChartAxis::setTickLabelRotation(double degrees) {
  if (!qFuzzyIsNull(degrees - mAxisPainter->tickLabelRotation)) {
    mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
    mCachedMarginValid = false;
  }
}

/*!
  Sets whether the tick labels (numbers) shall appear inside or outside the axis
  rect.

  The usual and default setting is \ref lsOutside. Very compact plots sometimes
  require tick labels to be inside the axis rect, to save space. If \a side is
  set to \ref lsInside, the tick labels appear on the inside are additionally
  clipped to the axis rect.
*/
void BaseChartAxis::setTickLabelSide(LabelSide side) {
  mAxisPainter->tickLabelSide = side;
  mCachedMarginValid = false;
}

/*!
  Sets the number format for the numbers in tick labels. This \a formatCode is
  an extended version of the format code used e.g. by QString::number() and
  QLocale::toString(). For reference about that, see the "Argument Formats"
  section in the detailed description of the QString class.

  \a formatCode is a string of one, two or three characters.

  <b>The first character</b> is identical to
  the normal format code used by Qt. In short, this means: 'e'/'E' scientific
  format, 'f' fixed format, 'g'/'G' scientific or fixed, whichever is shorter.
  For the 'e', 'E', and 'f' formats, the precision set by \ref
  setNumberPrecision represents the number of digits after the decimal point.
  For the 'g' and 'G' formats, the precision represents the maximum number of
  significant digits, trailing zeroes are omitted.

  <b>The second and third characters</b> are optional and specific to
  BaseChartCustomPlot:\n If the first char was 'e' or 'g', numbers are/might be
  displayed in the scientific format, e.g. "5.5e9", which is ugly in a plot. So
  when the second char of \a formatCode is set to 'b' (for "beautiful"), those
  exponential numbers are formatted in a more natural way, i.e. "5.5
  [multiplication sign] 10 [superscript] 9". By default, the multiplication sign
  is a centered dot. If instead a cross should be shown (as is usual in the
  USA), the third char of \a formatCode can be set to 'c'. The inserted
  multiplication signs are the UTF-8 characters 215 (0xD7) for the cross and 183
  (0xB7) for the dot.

  Examples for \a formatCode:
  \li \c g normal format code behaviour. If number is small, fixed format is
  used, if number is large, normal scientific format is used \li \c gb If number
  is small, fixed format is used, if number is large, scientific format is used
  with beautifully typeset decimal powers and a dot as multiplication sign \li
  \c ebc All numbers are in scientific format with beautifully typeset decimal
  power and a cross as multiplication sign \li \c fb illegal format code, since
  fixed format doesn't support (or need) beautifully typeset decimal powers.
  Format code will be reduced to 'f'. \li \c hello illegal format code, since
  first char is not 'e', 'E', 'f', 'g' or 'G'. Current format code will not be
  changed.
*/
void BaseChartAxis::setNumberFormat(const QString& formatCode) {
  if (formatCode.isEmpty()) {
    qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
    return;
  }
  mCachedMarginValid = false;

  // interpret first char as number format char:
  QString allowedFormatChars(QLatin1String("eEfgG"));
  if (allowedFormatChars.contains(formatCode.at(0))) {
    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
  } else {
    qDebug() << Q_FUNC_INFO
             << "Invalid number format code (first char not in 'eEfgG'):"
             << formatCode;
    return;
  }
  if (formatCode.length() < 2) {
    mNumberBeautifulPowers = false;
    mAxisPainter->numberMultiplyCross = false;
    return;
  }

  // interpret second char as indicator for beautiful decimal powers:
  if (formatCode.at(1) == QLatin1Char('b') &&
      (mNumberFormatChar == QLatin1Char('e') ||
       mNumberFormatChar == QLatin1Char('g'))) {
    mNumberBeautifulPowers = true;
  } else {
    qDebug() << Q_FUNC_INFO
             << "Invalid number format code (second char not 'b' or first char "
                "neither 'e' nor 'g'):"
             << formatCode;
    return;
  }
  if (formatCode.length() < 3) {
    mAxisPainter->numberMultiplyCross = false;
    return;
  }

  // interpret third char as indicator for dot or cross multiplication symbol:
  if (formatCode.at(2) == QLatin1Char('c')) {
    mAxisPainter->numberMultiplyCross = true;
  } else if (formatCode.at(2) == QLatin1Char('d')) {
    mAxisPainter->numberMultiplyCross = false;
  } else {
    qDebug() << Q_FUNC_INFO
             << "Invalid number format code (third char neither 'c' nor 'd'):"
             << formatCode;
    return;
  }
}

/*!
  Sets the precision of the tick label numbers. See QLocale::toString(double i,
  char f, int prec) for details. The effect of precisions are most notably for
  number Formats starting with 'e', see \ref setNumberFormat
*/
void BaseChartAxis::setNumberPrecision(int precision) {
  if (mNumberPrecision != precision) {
    mNumberPrecision = precision;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the length of the ticks in pixels. \a inside is the length the ticks will
  reach inside the plot and \a outside is the length they will reach outside the
  plot. If \a outside is greater than zero, the tick labels and axis label will
  increase their distance to the axis accordingly, so they won't collide with
  the ticks.

  \see setSubTickLength, setTickLengthIn, setTickLengthOut
*/
void BaseChartAxis::setTickLength(int inside, int outside) {
  setTickLengthIn(inside);
  setTickLengthOut(outside);
}

/*!
  Sets the length of the inward ticks in pixels. \a inside is the length the
  ticks will reach inside the plot.

  \see setTickLengthOut, setTickLength, setSubTickLength
*/
void BaseChartAxis::setTickLengthIn(int inside) {
  if (mAxisPainter->tickLengthIn != inside) {
    mAxisPainter->tickLengthIn = inside;
  }
}

/*!
  Sets the length of the outward ticks in pixels. \a outside is the length the
  ticks will reach outside the plot. If \a outside is greater than zero, the
  tick labels and axis label will increase their distance to the axis
  accordingly, so they won't collide with the ticks.

  \see setTickLengthIn, setTickLength, setSubTickLength
*/
void BaseChartAxis::setTickLengthOut(int outside) {
  if (mAxisPainter->tickLengthOut != outside) {
    mAxisPainter->tickLengthOut = outside;
    mCachedMarginValid = false;  // only outside tick length can change margin
  }
}

/*!
  Sets whether sub tick marks are displayed.

  Sub ticks are only potentially visible if (major) ticks are also visible (see
  \ref setTicks)

  \see setTicks
*/
void BaseChartAxis::setSubTicks(bool show) {
  if (mSubTicks != show) {
    mSubTicks = show;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the length of the subticks in pixels. \a inside is the length the
  subticks will reach inside the plot and \a outside is the length they will
  reach outside the plot. If \a outside is greater than zero, the tick labels
  and axis label will increase their distance to the axis accordingly, so they
  won't collide with the ticks.

  \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
*/
void BaseChartAxis::setSubTickLength(int inside, int outside) {
  setSubTickLengthIn(inside);
  setSubTickLengthOut(outside);
}

/*!
  Sets the length of the inward subticks in pixels. \a inside is the length the
  subticks will reach inside the plot.

  \see setSubTickLengthOut, setSubTickLength, setTickLength
*/
void BaseChartAxis::setSubTickLengthIn(int inside) {
  if (mAxisPainter->subTickLengthIn != inside) {
    mAxisPainter->subTickLengthIn = inside;
  }
}

/*!
  Sets the length of the outward subticks in pixels. \a outside is the length
  the subticks will reach outside the plot. If \a outside is greater than zero,
  the tick labels will increase their distance to the axis accordingly, so they
  won't collide with the ticks.

  \see setSubTickLengthIn, setSubTickLength, setTickLength
*/
void BaseChartAxis::setSubTickLengthOut(int outside) {
  if (mAxisPainter->subTickLengthOut != outside) {
    mAxisPainter->subTickLengthOut = outside;
    mCachedMarginValid = false;  // only outside tick length can change margin
  }
}

/*!
  Sets the pen, the axis base line is drawn with.

  \see setTickPen, setSubTickPen
*/
void BaseChartAxis::setBasePen(const QPen& pen) { mBasePen = pen; }

/*!
  Sets the pen, tick marks will be drawn with.

  \see setTickLength, setBasePen
*/
void BaseChartAxis::setTickPen(const QPen& pen) { mTickPen = pen; }

/*!
  Sets the pen, subtick marks will be drawn with.

  \see setSubTickCount, setSubTickLength, setBasePen
*/
void BaseChartAxis::setSubTickPen(const QPen& pen) { mSubTickPen = pen; }

/*!
  Sets the font of the axis label.

  \see setLabelColor
*/
void BaseChartAxis::setLabelFont(const QFont& font) {
  if (mLabelFont != font) {
    mLabelFont = font;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the color of the axis label.

  \see setLabelFont
*/
void BaseChartAxis::setLabelColor(const QColor& color) { mLabelColor = color; }

/*!
  Sets the text of the axis label that will be shown below/above or next to the
  axis, depending on its orientation. To disable axis labels, pass an empty
  string as \a str.
*/
void BaseChartAxis::setLabel(const QString& str) {
  if (mLabel != str) {
    mLabel = str;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the distance between the tick labels and the axis label.

  \see setTickLabelPadding, setPadding
*/
void BaseChartAxis::setLabelPadding(int padding) {
  if (mAxisPainter->labelPadding != padding) {
    mAxisPainter->labelPadding = padding;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the padding of the axis.

  When \ref BaseChartAxisRect::setAutoMargins is enabled, the padding is the
  additional outer most space, that is left blank.

  The axis padding has no meaning if \ref BaseChartAxisRect::setAutoMargins is
  disabled.

  \see setLabelPadding, setTickLabelPadding
*/
void BaseChartAxis::setPadding(int padding) {
  if (mPadding != padding) {
    mPadding = padding;
    mCachedMarginValid = false;
  }
}

/*!
  Sets the offset the axis has to its axis rect side.

  If an axis rect side has multiple axes and automatic margin calculation is
  enabled for that side, only the offset of the inner most axis has meaning
  (even if it is set to be invisible). The offset of the other, outer axes is
  controlled automatically, to place them at appropriate positions.
*/
void BaseChartAxis::setOffset(int offset) { mAxisPainter->offset = offset; }

/*!
  Sets the font that is used for tick labels when they are selected.

  \see setTickLabelFont, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedTickLabelFont(const QFont& font) {
  if (font != mSelectedTickLabelFont) {
    mSelectedTickLabelFont = font;
    // don't set mCachedMarginValid to false here because margin calculation is
    // always done with non-selected fonts
  }
}

/*!
  Sets the font that is used for the axis label when it is selected.

  \see setLabelFont, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedLabelFont(const QFont& font) {
  mSelectedLabelFont = font;
  // don't set mCachedMarginValid to false here because margin calculation is
  // always done with non-selected fonts
}

/*!
  Sets the color that is used for tick labels when they are selected.

  \see setTickLabelColor, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedTickLabelColor(const QColor& color) {
  if (color != mSelectedTickLabelColor) {
    mSelectedTickLabelColor = color;
  }
}

/*!
  Sets the color that is used for the axis label when it is selected.

  \see setLabelColor, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedLabelColor(const QColor& color) {
  mSelectedLabelColor = color;
}

/*!
  Sets the pen that is used to draw the axis base line when selected.

  \see setBasePen, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedBasePen(const QPen& pen) {
  mSelectedBasePen = pen;
}

/*!
  Sets the pen that is used to draw the (major) ticks when selected.

  \see setTickPen, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedTickPen(const QPen& pen) {
  mSelectedTickPen = pen;
}

/*!
  Sets the pen that is used to draw the subticks when selected.

  \see setSubTickPen, setSelectableParts, setSelectedParts,
  BaseChartCustomPlot::setInteractions
*/
void BaseChartAxis::setSelectedSubTickPen(const QPen& pen) {
  mSelectedSubTickPen = pen;
}

/*!
  Sets the style for the lower axis ending. See the documentation of
  BaseChartLineEnding for available styles.

  For horizontal axes, this method refers to the left ending, for vertical axes
  the bottom ending. Note that this meaning does not change when the axis range
  is reversed with \ref setRangeReversed.

  \see setUpperEnding
*/
void BaseChartAxis::setLowerEnding(const BaseChartLineEnding& ending) {
  mAxisPainter->lowerEnding = ending;
}

/*!
  Sets the style for the upper axis ending. See the documentation of
  BaseChartLineEnding for available styles.

  For horizontal axes, this method refers to the right ending, for vertical axes
  the top ending. Note that this meaning does not change when the axis range is
  reversed with \ref setRangeReversed.

  \see setLowerEnding
*/
void BaseChartAxis::setUpperEnding(const BaseChartLineEnding& ending) {
  mAxisPainter->upperEnding = ending;
}

void BaseChartAxis::setYAxis(BaseChartAxis* axis) { mYAxis = axis; }

void BaseChartAxis::setCustomGrid() { mAxisPainter->setCustomGrid(); }

/*!
  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to
  the lower and upper bounds of the range. The range is simply moved by \a diff.

  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a
  diff. This corresponds to an apparent "linear" move in logarithmic scaling by
  a distance of log(diff).
*/
void BaseChartAxis::moveRange(double diff) {
  BaseChartRange oldRange = mRange;
  if (mScaleType == stLinear) {
    mRange.lower += diff;
    mRange.upper += diff;
  } else  // mScaleType == stLogarithmic
  {
    mRange.lower *= diff;
    mRange.upper *= diff;
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  Scales the range of this axis by \a factor around the center of the current
  axis range. For example, if \a factor is 2.0, then the axis range will double
  its size, and the point at the axis range center won't have changed its
  position in the BaseChartCustomPlot widget (i.e. coordinates around the center
  will have moved symmetrically closer).

  If you wish to scale around a different coordinate than the current axis range
  center, use the overload \ref scaleRange(double factor, double center).
*/
void BaseChartAxis::scaleRange(double factor) {
  scaleRange(factor, range().center());
}

/*! \overload

  Scales the range of this axis by \a factor around the coordinate \a center.
  For example, if \a factor is 2.0, \a center is 1.0, then the axis range will
  double its size, and the point at coordinate 1.0 won't have changed its
  position in the BaseChartCustomPlot widget (i.e. coordinates around 1.0 will
  have moved symmetrically closer to 1.0).

  \see scaleRange(double factor)
*/
void BaseChartAxis::scaleRange(double factor, double center) {
  BaseChartRange oldRange = mRange;
  if (mScaleType == stLinear) {
    BaseChartRange newRange;
    newRange.lower = (mRange.lower - center) * factor + center;
    newRange.upper = (mRange.upper - center) * factor + center;
    if (BaseChartRange::validRange(newRange))
      mRange = newRange.sanitizedForLinScale();
  } else  // mScaleType == stLogarithmic
  {
    if ((mRange.upper < 0 && center < 0) ||
        (mRange.upper > 0 &&
         center > 0))  // make sure center has same sign as range
    {
      BaseChartRange newRange;
      newRange.lower = qPow(mRange.lower / center, factor) * center;
      newRange.upper = qPow(mRange.upper / center, factor) * center;
      if (BaseChartRange::validRange(newRange))
        mRange = newRange.sanitizedForLogScale();
    } else
      qDebug() << Q_FUNC_INFO
               << "Center of scaling operation doesn't lie in same logarithmic "
                  "sign domain as range:"
               << center;
  }
  emit rangeChanged(mRange);
  emit rangeChanged(mRange, oldRange);
}

/*!
  Scales the range of this axis to have a certain scale \a ratio to \a
  otherAxis. The scaling will be done around the center of the current axis
  range.

  For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is
  \a xAxis, graphs plotted with those axes will appear in a 1:1 aspect ratio,
  independent of the aspect ratio the axis rect has.

  This is an operation that changes the range of this axis once, it doesn't fix
  the scale ratio indefinitely. Note that calling this function in the
  constructor of the BaseChartCustomPlot's parent won't have the desired effect,
  since the widget dimensions aren't defined yet, and a resizeEvent will follow.
*/
void BaseChartAxis::setScaleRatio(const BaseChartAxis* otherAxis,
                                  double ratio) {
  int otherPixelSize, ownPixelSize;

  if (otherAxis->orientation() == Qt::Horizontal)
    otherPixelSize = otherAxis->axisRect()->width();
  else
    otherPixelSize = otherAxis->axisRect()->height();

  if (orientation() == Qt::Horizontal)
    ownPixelSize = axisRect()->width();
  else
    ownPixelSize = axisRect()->height();

  double newRangeSize =
      ratio * otherAxis->range().size() * ownPixelSize / double(otherPixelSize);
  setRange(range().center(), newRangeSize, Qt::AlignCenter);
}

/*!
  Changes the axis range such that all plottables associated with this axis are
  fully visible in that dimension.

  \see BaseChartAbstractPlottable::rescaleAxes, BaseChartCustomPlot::rescaleAxes
*/
void BaseChartAxis::rescale(bool onlyVisiblePlottables) {
  BaseChartRange newRange;
  bool haveRange = false;
  foreach (BaseChartAbstractPlottable* plottable, plottables()) {
    if (!plottable->realVisibility() && onlyVisiblePlottables) continue;
    BaseChartRange plottableRange;
    bool currentFoundRange;
    SignDomain signDomain = sdBoth;
    if (mScaleType == stLogarithmic)
      signDomain = (mRange.upper < 0 ? sdNegative : sdPositive);
    if (plottable->keyAxis() == this)
      plottableRange = plottable->getKeyRange(currentFoundRange, signDomain);
    else
      plottableRange = plottable->getValueRange(currentFoundRange, signDomain);
    if (currentFoundRange) {
      if (!haveRange)
        newRange = plottableRange;
      else
        newRange.expand(plottableRange);
      haveRange = true;
    }
  }
  if (haveRange) {
    if (!BaseChartRange::validRange(
            newRange))  // likely due to range being zero (plottable has only
                        // constant data in this axis dimension), shift current
                        // range to at least center the plottable
    {
      double center =
          (newRange.lower + newRange.upper) *
          0.5;  // upper and lower should be equal anyway, but just to make
                // sure, incase validRange returned false for other reason
      if (mScaleType == stLinear) {
        newRange.lower = center - mRange.size() / 2.0;
        newRange.upper = center + mRange.size() / 2.0;
      } else  // mScaleType == stLogarithmic
      {
        newRange.lower = center / qSqrt(mRange.upper / mRange.lower);
        newRange.upper = center * qSqrt(mRange.upper / mRange.lower);
      }
    }
    setRange(newRange);
  }
}

/*!
  Transforms \a value, in pixel coordinates of the BaseChartCustomPlot widget,
  to axis coordinates.
*/
double BaseChartAxis::pixelToCoord(double value) const {
  if (orientation() == Qt::Horizontal) {
    if (mScaleType == stLinear) {
      if (!mRangeReversed)
        return (value - mAxisRect->left()) / double(mAxisRect->width()) *
                   mRange.size() +
               mRange.lower;
      else
        return -(value - mAxisRect->left()) / double(mAxisRect->width()) *
                   mRange.size() +
               mRange.upper;
    } else  // mScaleType == stLogarithmic
    {
      if (!mRangeReversed)
        return qPow(mRange.upper / mRange.lower,
                    (value - mAxisRect->left()) / double(mAxisRect->width())) *
               mRange.lower;
      else
        return qPow(mRange.upper / mRange.lower,
                    (mAxisRect->left() - value) / double(mAxisRect->width())) *
               mRange.upper;
    }
  } else  // orientation() == Qt::Vertical
  {
    if (mScaleType == stLinear) {
      if (!mRangeReversed)
        return (mAxisRect->bottom() - value) / double(mAxisRect->height()) *
                   mRange.size() +
               mRange.lower;
      else
        return -(mAxisRect->bottom() - value) / double(mAxisRect->height()) *
                   mRange.size() +
               mRange.upper;
    } else  // mScaleType == stLogarithmic
    {
      if (!mRangeReversed)
        return qPow(mRange.upper / mRange.lower,
                    (mAxisRect->bottom() - value) /
                        double(mAxisRect->height())) *
               mRange.lower;
      else
        return qPow(mRange.upper / mRange.lower,
                    (value - mAxisRect->bottom()) /
                        double(mAxisRect->height())) *
               mRange.upper;
    }
  }
}

/*!
  Transforms \a value, in coordinates of the axis, to pixel coordinates of the
  BaseChartCustomPlot widget.
*/
double BaseChartAxis::coordToPixel(double value) const {
  if (orientation() == Qt::Horizontal) {
    if (mScaleType == stLinear) {
      if (!mRangeReversed)
        return (value - mRange.lower) / mRange.size() * mAxisRect->width() +
               mAxisRect->left();
      else
        return (mRange.upper - value) / mRange.size() * mAxisRect->width() +
               mAxisRect->left();
    } else  // mScaleType == stLogarithmic
    {
      if (value >= 0.0 &&
          mRange.upper < 0.0)  // invalid value for logarithmic scale, just draw
                               // it outside visible range
        return !mRangeReversed ? mAxisRect->right() + 200
                               : mAxisRect->left() - 200;
      else if (value <= 0.0 &&
               mRange.upper >= 0.0)  // invalid value for logarithmic scale,
                                     // just draw it outside visible range
        return !mRangeReversed ? mAxisRect->left() - 200
                               : mAxisRect->right() + 200;
      else {
        if (!mRangeReversed)
          return qLn(value / mRange.lower) / qLn(mRange.upper / mRange.lower) *
                     mAxisRect->width() +
                 mAxisRect->left();
        else
          return qLn(mRange.upper / value) / qLn(mRange.upper / mRange.lower) *
                     mAxisRect->width() +
                 mAxisRect->left();
      }
    }
  } else  // orientation() == Qt::Vertical
  {
    if (mScaleType == stLinear) {
      if (!mRangeReversed)
        return mAxisRect->bottom() -
               (value - mRange.lower) / mRange.size() * mAxisRect->height();
      else
        return mAxisRect->bottom() -
               (mRange.upper - value) / mRange.size() * mAxisRect->height();
    } else  // mScaleType == stLogarithmic
    {
      if (value >= 0.0 &&
          mRange.upper < 0.0)  // invalid value for logarithmic scale, just draw
                               // it outside visible range
        return !mRangeReversed ? mAxisRect->top() - 200
                               : mAxisRect->bottom() + 200;
      else if (value <= 0.0 &&
               mRange.upper >= 0.0)  // invalid value for logarithmic scale,
                                     // just draw it outside visible range
        return !mRangeReversed ? mAxisRect->bottom() + 200
                               : mAxisRect->top() - 200;
      else {
        if (!mRangeReversed)
          return mAxisRect->bottom() - qLn(value / mRange.lower) /
                                           qLn(mRange.upper / mRange.lower) *
                                           mAxisRect->height();
        else
          return mAxisRect->bottom() - qLn(mRange.upper / value) /
                                           qLn(mRange.upper / mRange.lower) *
                                           mAxisRect->height();
      }
    }
  }
}

/*!
  Returns the part of the axis that is hit by \a pos (in pixels). The return
  value of this function is independent of the user-selectable parts defined
  with \ref setSelectableParts. Further, this function does not change the
  current selection state of the axis.

  If the axis is not visible (\ref setVisible), this function always returns
  \ref spNone.

  \see setSelectedParts, setSelectableParts,
  BaseChartCustomPlot::setInteractions
*/
BaseChartAxis::SelectablePart BaseChartAxis::getPartAt(
    const QPointF& pos) const {
  if (!mVisible) return spNone;

  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
    return spAxis;
  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
    return spTickLabels;
  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
    return spAxisLabel;
  else
    return spNone;
}

/* inherits documentation from base class */
double BaseChartAxis::selectTest(const QPointF& pos, bool onlySelectable,
                                 QVariant* details) const {
  if (!mParentPlot) return -1;
  SelectablePart part = getPartAt(pos);
  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
    return -1;

  if (details) details->setValue(part);
  return mParentPlot->selectionTolerance() * 0.99;
}

/*!
  Returns a list of all the plottables that have this axis as key or value axis.

  If you are only interested in plottables of type BaseChartGraph, see \ref
  graphs.

  \see graphs, items
*/
QList<BaseChartAbstractPlottable*> BaseChartAxis::plottables() const {
  QList<BaseChartAbstractPlottable*> result;
  if (!mParentPlot) return result;

  foreach (BaseChartAbstractPlottable* plottable, mParentPlot->mPlottables) {
    if (plottable->keyAxis() == this || plottable->valueAxis() == this)
      result.append(plottable);
  }
  return result;
}

/*!
  Returns a list of all the graphs that have this axis as key or value axis.

  \see plottables, items
*/
QList<BaseChartGraph*> BaseChartAxis::graphs() const {
  QList<BaseChartGraph*> result;
  if (!mParentPlot) return result;

  foreach (BaseChartGraph* graph, mParentPlot->mGraphs) {
    if (graph->keyAxis() == this || graph->valueAxis() == this)
      result.append(graph);
  }
  return result;
}

/*!
  Returns a list of all the items that are associated with this axis. An item is
  considered associated with an axis if at least one of its positions uses the
  axis as key or value axis.

  \see plottables, graphs
*/
QList<BaseChartAbstractItem*> BaseChartAxis::items() const {
  QList<BaseChartAbstractItem*> result;
  if (!mParentPlot) return result;

  foreach (BaseChartAbstractItem* item, mParentPlot->mItems) {
    foreach (BaseChartItemPosition* position, item->positions()) {
      if (position->keyAxis() == this || position->valueAxis() == this) {
        result.append(item);
        break;
      }
    }
  }
  return result;
}

/*!
  Transforms a margin side to the logically corresponding axis type. (msLeft to
  BaseChartAxis::atLeft, msRight to BaseChartAxis::atRight, etc.)
*/
BaseChartAxis::AxisType BaseChartAxis::marginSideToAxisType(MarginSide side) {
  switch (side) {
    case msLeft:
      return atLeft;
    case msRight:
      return atRight;
    case msTop:
      return atTop;
    case msBottom:
      return atBottom;
    default:
      break;
  }
  qDebug() << Q_FUNC_INFO
           << "Invalid margin side passed:" << static_cast<int>(side);
  return atLeft;
}

/*!
  Returns the axis type that describes the opposite axis of an axis with the
  specified \a type.
*/
BaseChartAxis::AxisType BaseChartAxis::opposite(BaseChartAxis::AxisType type) {
  switch (type) {
    case atLeft:
      return atRight;
    case atRight:
      return atLeft;
    case atBottom:
      return atTop;
    case atTop:
      return atBottom;
  }
  qDebug() << Q_FUNC_INFO << "invalid axis type";
  return atLeft;
}

/* inherits documentation from base class */
void BaseChartAxis::selectEvent(QMouseEvent* event, bool additive,
                                const QVariant& details,
                                bool* selectionStateChanged) {
  Q_UNUSED(event)
  SelectablePart part = details.value<SelectablePart>();
  if (mSelectableParts.testFlag(part)) {
    SelectableParts selBefore = mSelectedParts;
    setSelectedParts(additive ? mSelectedParts ^ part : part);
    if (selectionStateChanged)
      *selectionStateChanged = mSelectedParts != selBefore;
  }
}

/* inherits documentation from base class */
void BaseChartAxis::deselectEvent(bool* selectionStateChanged) {
  SelectableParts selBefore = mSelectedParts;
  setSelectedParts(mSelectedParts & ~mSelectableParts);
  if (selectionStateChanged)
    *selectionStateChanged = mSelectedParts != selBefore;
}

/*! \internal

  This mouse event reimplementation provides the functionality to let the user
  drag individual axes exclusively, by startig the drag on top of the axis.

  For the axis to accept this event and perform the single axis drag, the parent
  \ref BaseChartAxisRect must be configured accordingly, i.e. it must allow
  range dragging in the orientation of this axis
  (\ref BaseChartAxisRect::setRangeDrag) and this axis must be a draggable axis
  (\ref BaseChartAxisRect::setRangeDragAxes)

  \seebaseclassmethod

  \note The dragging of possibly multiple axes at once by starting the drag
  anywhere in the axis rect is handled by the axis rect's mouse event, e.g. \ref
  BaseChartAxisRect::mousePressEvent.
*/
void BaseChartAxis::mousePressEvent(QMouseEvent* event,
                                    const QVariant& details) {
  Q_UNUSED(details)
  if (!mParentPlot->interactions().testFlag(iRangeDrag) ||
      !mAxisRect->rangeDrag().testFlag(orientation()) ||
      !mAxisRect->rangeDragAxes(orientation()).contains(this)) {
    event->ignore();
    return;
  }

  if (event->buttons() & Qt::LeftButton) {
    mDragging = true;
    // initialize antialiasing backup in case we start dragging:
    if (mParentPlot->noAntialiasingOnDrag()) {
      mAADragBackup = mParentPlot->antialiasedElements();
      mNotAADragBackup = mParentPlot->notAntialiasedElements();
    }
    // Mouse range dragging interaction:
    if (mParentPlot->interactions().testFlag(iRangeDrag))
      mDragStartRange = mRange;
  }
}

/*! \internal

  This mouse event reimplementation provides the functionality to let the user
  drag individual axes exclusively, by startig the drag on top of the axis.

  \seebaseclassmethod

  \note The dragging of possibly multiple axes at once by starting the drag
  anywhere in the axis rect is handled by the axis rect's mouse event, e.g. \ref
  BaseChartAxisRect::mousePressEvent.

  \see BaseChartAxis::mousePressEvent
*/
void BaseChartAxis::mouseMoveEvent(QMouseEvent* event,
                                   const QPointF& startPos) {
  if (mDragging) {
    const double startPixel =
        orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
    const double currentPixel =
        orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();
    if (mScaleType == BaseChartAxis::stLinear) {
      const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);
      setRange(mDragStartRange.lower + diff, mDragStartRange.upper + diff);
    } else if (mScaleType == BaseChartAxis::stLogarithmic) {
      const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);
      setRange(mDragStartRange.lower * diff, mDragStartRange.upper * diff);
    }

    if (mParentPlot->noAntialiasingOnDrag())
      mParentPlot->setNotAntialiasedElements(aeAll);
    mParentPlot->replot(BaseChartCustomPlot::rpQueuedReplot);
  }
}

/*! \internal

  This mouse event reimplementation provides the functionality to let the user
  drag individual axes exclusively, by startig the drag on top of the axis.

  \seebaseclassmethod

  \note The dragging of possibly multiple axes at once by starting the drag
  anywhere in the axis rect is handled by the axis rect's mouse event, e.g. \ref
  BaseChartAxisRect::mousePressEvent.

  \see BaseChartAxis::mousePressEvent
*/
void BaseChartAxis::mouseReleaseEvent(QMouseEvent* event,
                                      const QPointF& startPos) {
  Q_UNUSED(event)
  Q_UNUSED(startPos)
  mDragging = false;

  if (mParentPlot->noAntialiasingOnDrag()) {
    mParentPlot->setAntialiasedElements(mAADragBackup);
    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
  }
}

/*! \internal

  This mouse event reimplementation provides the functionality to let the user
  zoom individual axes exclusively, by performing the wheel event on top of the
  axis.

  For the axis to accept this event and perform the single axis zoom, the parent
  \ref BaseChartAxisRect must be configured accordingly, i.e. it must allow
  range zooming in the orientation of this axis
  (\ref BaseChartAxisRect::setRangeZoom) and this axis must be a zoomable axis
  (\ref BaseChartAxisRect::setRangeZoomAxes)

  \seebaseclassmethod

  \note The zooming of possibly multiple axes at once by performing the wheel
  event anywhere in the axis rect is handled by the axis rect's mouse event,
  e.g. \ref BaseChartAxisRect::wheelEvent.
*/
void BaseChartAxis::wheelEvent(QWheelEvent* event) {
  // Mouse range zooming interaction:
  if (!mParentPlot->interactions().testFlag(iRangeZoom) ||
      !mAxisRect->rangeZoom().testFlag(orientation()) ||
      !mAxisRect->rangeZoomAxes(orientation()).contains(this)) {
    event->ignore();
    return;
  }

  const double delta = event->angleDelta().y();
  const QPointF pos = event->position();
  const double wheelSteps =
      delta / 120.0;  // a single step delta is +/-120 usually
  const double factor =
      qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
  scaleRange(factor,
             pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y()));
  mParentPlot->replot();
}

/*! \internal

  A convenience function to easily set the QPainter::Antialiased hint on the
  provided \a painter before drawing axis lines.

  This is the antialiasing state the painter passed to the \ref draw method is
  in by default.

  This function takes into account the local setting of the antialiasing flag as
  well as the overrides set with \ref
  BaseChartCustomPlot::setAntialiasedElements and \ref
  BaseChartCustomPlot::setNotAntialiasedElements.

  \seebaseclassmethod

  \see setAntialiased
*/
void BaseChartAxis::applyDefaultAntialiasingHint(
    BaseChartPainter* painter) const {
  applyAntialiasingHint(painter, mAntialiased, aeAxes);
}

/*! \internal

  Draws the axis with the specified \a painter, using the internal
  BaseChartAxisPainterPrivate instance.

  \seebaseclassmethod
*/
void BaseChartAxis::draw(BaseChartPainter* painter) {
  QVector<double> subTickPositions;  // the final coordToPixel transformed
                                     // vector passed to QCPAxisPainter
  QVector<double> tickPositions;  // the final coordToPixel transformed vector
                                  // passed to QCPAxisPainter
  QVector<QString> tickLabels;    // the final vector passed to QCPAxisPainter
  tickPositions.reserve(mTickVector.size());
  tickLabels.reserve(mTickVector.size());
  subTickPositions.reserve(mSubTickVector.size());

  if (mTicks) {
    for (int i = 0; i < mTickVector.size(); ++i) {
      tickPositions.append(coordToPixel(mTickVector.at(i)));
      if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
    }

    if (mSubTicks) {
      const int subTickCount = mSubTickVector.size();
      for (int i = 0; i < subTickCount; ++i)
        subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
    }
  }

  // transfer all properties of this axis to BaseChartAxisPainterPrivate which
  // it needs to draw the axis. Note that some axis painter properties are
  // already set by direct feed-through with BaseChartAxis setters
  mAxisPainter->type = mAxisType;
  mAxisPainter->basePen = getBasePen();
  mAxisPainter->labelFont = getLabelFont();
  mAxisPainter->labelColor = getLabelColor();
  mAxisPainter->label = mLabel;
  mAxisPainter->substituteExponent = mNumberBeautifulPowers;
  mAxisPainter->tickPen = getTickPen();
  mAxisPainter->subTickPen = getSubTickPen();
  mAxisPainter->tickLabelFont = getTickLabelFont();
  mAxisPainter->tickLabelColor = getTickLabelColor();
  mAxisPainter->axisRect = mAxisRect->rect();
  mAxisPainter->viewportRect = mParentPlot->viewport();
  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
  mAxisPainter->reversedEndings = mRangeReversed;
  mAxisPainter->tickPositions = tickPositions;
  mAxisPainter->tickLabels = tickLabels;
  mAxisPainter->subTickPositions = subTickPositions;
  mAxisPainter->draw(painter);
}

/*! \internal

  Prepares the internal tick vector, sub tick vector and tick label vector. This
  is done by calling BaseChartAxisTicker::generate on the currently installed
  ticker.

  If a change in the label text/count is detected, the cached axis margin is
  invalidated to make sure the next margin calculation recalculates the label
  sizes and returns an up-to-date value.
*/
void BaseChartAxis::setupTickVectors() {
  if (!mParentPlot) return;
  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0)
    return;

  QVector<QString> oldLabels = mTickVectorLabels;
  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar,
                    mNumberPrecision, mTickVector,
                    mSubTicks ? &mSubTickVector : nullptr,
                    mTickLabels ? &mTickVectorLabels : nullptr);
  mCachedMarginValid &=
      mTickVectorLabels ==
      oldLabels;  // if labels have changed, margin might have changed, too
}

/*! \internal

  Returns the pen that is used to draw the axis base line. Depending on the
  selection state, this is either mSelectedBasePen or mBasePen.
*/
QPen BaseChartAxis::getBasePen() const {
  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
}

/*! \internal

  Returns the pen that is used to draw the (major) ticks. Depending on the
  selection state, this is either mSelectedTickPen or mTickPen.
*/
QPen BaseChartAxis::getTickPen() const {
  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
}

/*! \internal

  Returns the pen that is used to draw the subticks. Depending on the selection
  state, this is either mSelectedSubTickPen or mSubTickPen.
*/
QPen BaseChartAxis::getSubTickPen() const {
  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
}

/*! \internal

  Returns the font that is used to draw the tick labels. Depending on the
  selection state, this is either mSelectedTickLabelFont or mTickLabelFont.
*/
QFont BaseChartAxis::getTickLabelFont() const {
  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont
                                               : mTickLabelFont;
}

/*! \internal

  Returns the font that is used to draw the axis label. Depending on the
  selection state, this is either mSelectedLabelFont or mLabelFont.
*/
QFont BaseChartAxis::getLabelFont() const {
  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
}

/*! \internal

  Returns the color that is used to draw the tick labels. Depending on the
  selection state, this is either mSelectedTickLabelColor or mTickLabelColor.
*/
QColor BaseChartAxis::getTickLabelColor() const {
  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor
                                               : mTickLabelColor;
}

/*! \internal

  Returns the color that is used to draw the axis label. Depending on the
  selection state, this is either mSelectedLabelColor or mLabelColor.
*/
QColor BaseChartAxis::getLabelColor() const {
  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor
                                              : mLabelColor;
}

/*! \internal

  Returns the appropriate outward margin for this axis. It is needed if \ref
  BaseChartAxisRect::setAutoMargins is set to true on the parent axis rect. An
  axis with axis type \ref atLeft will return an appropriate left margin, \ref
  atBottom will return an appropriate bottom margin and so forth. For the
  calculation, this function goes through similar steps as \ref draw, so
  changing one function likely requires the modification of the other one as
  well.

  The margin consists of the outward tick length, tick label padding, tick label
  size, label padding, label size, and padding.

  The margin is cached internally, so repeated calls while leaving the axis
  range, fonts, etc. unchanged are very fast.
*/
int BaseChartAxis::calculateMargin() {
  if (!mVisible)  // if not visible, directly return 0, don't cache 0 because we
                  // can't react to setVisible in BaseChartAxis
    return 0;

  if (mCachedMarginValid) return mCachedMargin;

  // run through similar steps as BaseChartAxis::draw, and calculate margin
  // needed to fit axis and its labels
  int margin = 0;

  QVector<double> tickPositions;  // the final coordToPixel transformed vector
                                  // passed to QCPAxisPainter
  QVector<QString> tickLabels;    // the final vector passed to QCPAxisPainter
  tickPositions.reserve(mTickVector.size());
  tickLabels.reserve(mTickVector.size());

  if (mTicks) {
    for (int i = 0; i < mTickVector.size(); ++i) {
      tickPositions.append(coordToPixel(mTickVector.at(i)));
      if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
    }
  }
  // transfer all properties of this axis to BaseChartAxisPainterPrivate which
  // it needs to calculate the size. Note that some axis painter properties are
  // already set by direct feed-through with BaseChartAxis setters
  mAxisPainter->type = mAxisType;
  mAxisPainter->labelFont = getLabelFont();
  mAxisPainter->label = mLabel;
  mAxisPainter->tickLabelFont = mTickLabelFont;
  mAxisPainter->axisRect = mAxisRect->rect();
  mAxisPainter->viewportRect = mParentPlot->viewport();
  mAxisPainter->tickPositions = tickPositions;
  mAxisPainter->tickLabels = tickLabels;
  margin += mAxisPainter->size();
  margin += mPadding;

  mCachedMargin = margin;
  mCachedMarginValid = true;
  return margin;
}

/* inherits documentation from base class */
Interaction BaseChartAxis::selectionCategory() const { return iSelectAxes; }

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisRect
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartAxisRect
  \brief Holds multiple axes and arranges them in a rectangular shape.

  This class represents an axis rect, a rectangular area that is bounded on all
  sides with an arbitrary number of axes.

  Initially BaseChartCustomPlot has one axis rect, accessible via
  BaseChartCustomPlot::axisRect(). However, the layout system allows to have
  multiple axis rects, e.g. arranged in a grid layout
  (BaseChartCustomPlot::plotLayout).

  By default, BaseChartAxisRect comes with four axes, at bottom, top, left and
  right. They can be accessed via \ref axis by providing the respective axis
  type (\ref BaseChartAxis::AxisType) and index. If you need all axes in the
  axis rect, use \ref axes. The top and right axes are set to be invisible
  initially (BaseChartAxis::setVisible). To add more axes to a side, use \ref
  addAxis or \ref addAxes. To remove an axis, use \ref removeAxis.

  The axis rect layerable itself only draws a background pixmap or color, if
  specified (\ref setBackground). It is placed on the "background" layer
  initially (see \ref QCPLayer for an explanation of the BaseChartCustomPlot
  layer system). The axes that are held by the axis rect can be placed on other
  layers, independently of the axis rect.

  Every axis rect has a child layout of type \ref BaseChartLayoutInset. It is
  accessible via \ref insetLayout and can be used to have other layout elements
  (or even other layouts with multiple elements) hovering inside the axis rect.

  If an axis rect is clicked and dragged, it processes this by moving certain
  axis ranges. The behaviour can be controlled with \ref setRangeDrag and \ref
  setRangeDragAxes. If the mouse wheel is scrolled while the cursor is on the
  axis rect, certain axes are scaled. This is controllable via \ref
  setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These
  interactions are only enabled if \ref BaseChartCustomPlot::setInteractions
  contains \ref iRangeDrag and \ref iRangeZoom.

  \image html AxisRectSpacingOverview.png
  <center>Overview of the spacings and paddings that define the geometry of an
  axis. The dashed line on the far left indicates the viewport/widget
  border.</center>
*/

/* start documentation of inline functions */

/*! \fn BaseChartLayoutInset *BaseChartAxisRect::insetLayout() const

  Returns the inset layout of this axis rect. It can be used to place other
  layout elements (or even layouts with multiple other elements) inside/on top
  of an axis rect.

  \see BaseChartLayoutInset
*/

/*! \fn int BaseChartAxisRect::left() const

  Returns the pixel position of the left border of this axis rect. Margins are
  not taken into account here, so the returned value is with respect to the
  inner \ref rect.
*/

/*! \fn int BaseChartAxisRect::right() const

  Returns the pixel position of the right border of this axis rect. Margins are
  not taken into account here, so the returned value is with respect to the
  inner \ref rect.
*/

/*! \fn int BaseChartAxisRect::top() const

  Returns the pixel position of the top border of this axis rect. Margins are
  not taken into account here, so the returned value is with respect to the
  inner \ref rect.
*/

/*! \fn int BaseChartAxisRect::bottom() const

  Returns the pixel position of the bottom border of this axis rect. Margins are
  not taken into account here, so the returned value is with respect to the
  inner \ref rect.
*/

/*! \fn int BaseChartAxisRect::width() const

  Returns the pixel width of this axis rect. Margins are not taken into account
  here, so the returned value is with respect to the inner \ref rect.
*/

/*! \fn int BaseChartAxisRect::height() const

  Returns the pixel height of this axis rect. Margins are not taken into account
  here, so the returned value is with respect to the inner \ref rect.
*/

/*! \fn QSize BaseChartAxisRect::size() const

  Returns the pixel size of this axis rect. Margins are not taken into account
  here, so the returned value is with respect to the inner \ref rect.
*/

/*! \fn QPoint BaseChartAxisRect::topLeft() const

  Returns the top left corner of this axis rect in pixels. Margins are not taken
  into account here, so the returned value is with respect to the inner \ref
  rect.
*/

/*! \fn QPoint BaseChartAxisRect::topRight() const

  Returns the top right corner of this axis rect in pixels. Margins are not
  taken into account here, so the returned value is with respect to the inner
  \ref rect.
*/

/*! \fn QPoint BaseChartAxisRect::bottomLeft() const

  Returns the bottom left corner of this axis rect in pixels. Margins are not
  taken into account here, so the returned value is with respect to the inner
  \ref rect.
*/

/*! \fn QPoint BaseChartAxisRect::bottomRight() const

  Returns the bottom right corner of this axis rect in pixels. Margins are not
  taken into account here, so the returned value is with respect to the inner
  \ref rect.
*/

/*! \fn QPoint BaseChartAxisRect::center() const

  Returns the center of this axis rect in pixels. Margins are not taken into
  account here, so the returned value is with respect to the inner \ref rect.
*/

/* end documentation of inline functions */

/*!
  Creates a BaseChartAxisRect instance and sets default values. An axis is added
  for each of the four sides, the top and right axes are set invisible
  initially.
*/
BaseChartAxisRect::BaseChartAxisRect(BaseChartCustomPlot* parentPlot,
                                     bool setupDefaultAxes)
    : BaseChartLayoutElement(parentPlot),
      mBackgroundBrush(Qt::NoBrush),
      mBackgroundScaled(true),
      mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
      mInsetLayout(new BaseChartLayoutInset),
      mRangeDrag(Qt::Horizontal | Qt::Vertical),
      mRangeZoom(Qt::Horizontal | Qt::Vertical),
      mRangeZoomFactorHorz(0.85),
      mRangeZoomFactorVert(0.85),
      mDragging(false) {
  mInsetLayout->initializeParentPlot(mParentPlot);
  mInsetLayout->setParentLayerable(this);
  mInsetLayout->setParent(this);

  setMinimumSize(50, 50);
  setMinimumMargins(QMargins(0, 15, 2, 0));
  mAxes.insert(BaseChartAxis::atLeft, QList<BaseChartAxis*>());
  mAxes.insert(BaseChartAxis::atRight, QList<BaseChartAxis*>());
  mAxes.insert(BaseChartAxis::atTop, QList<BaseChartAxis*>());
  mAxes.insert(BaseChartAxis::atBottom, QList<BaseChartAxis*>());

  if (setupDefaultAxes) {
    BaseChartAxis* xAxis = addAxis(BaseChartAxis::atBottom);
    BaseChartAxis* yAxis = addAxis(BaseChartAxis::atLeft);
    BaseChartAxis* xAxis2 = addAxis(BaseChartAxis::atTop);
    BaseChartAxis* yAxis2 = addAxis(BaseChartAxis::atRight);
    setRangeDragAxes(xAxis, yAxis);
    setRangeZoomAxes(xAxis, yAxis);
    xAxis2->setVisible(false);
    yAxis2->setVisible(false);
    xAxis->grid()->setVisible(true);
    yAxis->grid()->setVisible(true);
    xAxis2->grid()->setVisible(false);
    yAxis2->grid()->setVisible(false);
    xAxis2->grid()->setZeroLinePen(Qt::NoPen);
    yAxis2->grid()->setZeroLinePen(Qt::NoPen);
    xAxis2->grid()->setVisible(false);
    yAxis2->grid()->setVisible(false);
  }
}

BaseChartAxisRect::~BaseChartAxisRect() {
  delete mInsetLayout;
  mInsetLayout = nullptr;

  foreach (BaseChartAxis* axis, axes()) removeAxis(axis);
}

/*!
  Returns the number of axes on the axis rect side specified with \a type.

  \see axis
*/
int BaseChartAxisRect::axisCount(BaseChartAxis::AxisType type) const {
  return mAxes.value(type).size();
}

/*!
  Returns the axis with the given \a index on the axis rect side specified with
  \a type.

  \see axisCount, axes
*/
BaseChartAxis* BaseChartAxisRect::axis(BaseChartAxis::AxisType type,
                                       int index) const {
  QList<BaseChartAxis*> ax(mAxes.value(type));
  if (index >= 0 && index < ax.size()) {
    return ax.at(index);
  } else {
    qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
    return nullptr;
  }
}

/*!
  Returns all axes on the axis rect sides specified with \a types.

  \a types may be a single \ref BaseChartAxis::AxisType or an
  <tt>or</tt>-combination, to get the axes of multiple sides.

  \see axis
*/
QList<BaseChartAxis*> BaseChartAxisRect::axes(
    BaseChartAxis::AxisTypes types) const {
  QList<BaseChartAxis*> result;
  if (types.testFlag(BaseChartAxis::atLeft))
    result << mAxes.value(BaseChartAxis::atLeft);
  if (types.testFlag(BaseChartAxis::atRight))
    result << mAxes.value(BaseChartAxis::atRight);
  if (types.testFlag(BaseChartAxis::atTop))
    result << mAxes.value(BaseChartAxis::atTop);
  if (types.testFlag(BaseChartAxis::atBottom))
    result << mAxes.value(BaseChartAxis::atBottom);
  return result;
}

/*! \overload

  Returns all axes of this axis rect.
*/
QList<BaseChartAxis*> BaseChartAxisRect::axes() const {
  QList<BaseChartAxis*> result;
  QHashIterator<BaseChartAxis::AxisType, QList<BaseChartAxis*> > it(mAxes);
  while (it.hasNext()) {
    it.next();
    result << it.value();
  }
  return result;
}

/*!
  Adds a new axis to the axis rect side specified with \a type, and returns it.
  If \a axis is 0, a new BaseChartAxis instance is created internally.
  BaseChartCustomPlot owns the returned axis, so if you want to remove an axis,
  use \ref removeAxis instead of deleting it manually.

  You may inject BaseChartAxis instances (or subclasses of BaseChartAxis) by
  setting \a axis to an axis that was previously created outside
  BaseChartCustomPlot. It is important to note that BaseChartCustomPlot takes
  ownership of the axis, so you may not delete it afterwards. Further, the \a
  axis must have been created with this axis rect as parent and with the same
  axis type as specified in \a type. If this is not the case, a debug output is
  generated, the axis is not added, and the method returns \c nullptr.

  This method can not be used to move \a axis between axis rects. The same \a
  axis instance must not be added multiple times to the same or different axis
  rects.

  If an axis rect side already contains one or more axes, the lower and upper
  endings of the new axis (\ref BaseChartAxis::setLowerEnding, \ref
  BaseChartAxis::setUpperEnding) are set to \ref BaseChartLineEnding::esHalfBar.

  \see addAxes, setupFullAxesBox
*/
BaseChartAxis* BaseChartAxisRect::addAxis(BaseChartAxis::AxisType type,
                                          BaseChartAxis* axis) {
  BaseChartAxis* newAxis = axis;
  if (!newAxis) {
    newAxis = new BaseChartAxis(this, type);
  } else  // user provided existing axis instance, do some sanity checks
  {
    if (newAxis->axisType() != type) {
      qDebug() << Q_FUNC_INFO
               << "passed axis has different axis type than specified in type "
                  "parameter";
      return nullptr;
    }
    if (newAxis->axisRect() != this) {
      qDebug() << Q_FUNC_INFO
               << "passed axis doesn't have this axis rect as parent axis rect";
      return nullptr;
    }
    if (axes().contains(newAxis)) {
      qDebug() << Q_FUNC_INFO
               << "passed axis is already owned by this axis rect";
      return nullptr;
    }
  }
  if (!mAxes[type].isEmpty())  // multiple axes on one side, add half-bar axis
                               // ending to additional axes with offset
  {
    bool invert =
        (type == BaseChartAxis::atRight) || (type == BaseChartAxis::atBottom);
    newAxis->setLowerEnding(
        BaseChartLineEnding(BaseChartLineEnding::esHalfBar, 6, 10, !invert));
    newAxis->setUpperEnding(
        BaseChartLineEnding(BaseChartLineEnding::esHalfBar, 6, 10, invert));
  }
  mAxes[type].append(newAxis);

  // reset convenience axis pointers on parent BaseChartCustomPlot if they are
  // unset:
  if (mParentPlot && mParentPlot->axisRectCount() > 0 &&
      mParentPlot->axisRect(0) == this) {
    switch (type) {
      case BaseChartAxis::atBottom: {
        if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis;
        break;
      }
      case BaseChartAxis::atLeft: {
        if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis;
        break;
      }
      case BaseChartAxis::atTop: {
        if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis;
        break;
      }
      case BaseChartAxis::atRight: {
        if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis;
        break;
      }
    }
  }

  return newAxis;
}

/*!
  Adds a new axis with \ref addAxis to each axis rect side specified in \a
  types. This may be an <tt>or</tt>-combination of BaseChartAxis::AxisType, so
  axes can be added to multiple sides at once.

  Returns a list of the added axes.

  \see addAxis, setupFullAxesBox
*/
QList<BaseChartAxis*> BaseChartAxisRect::addAxes(
    BaseChartAxis::AxisTypes types) {
  QList<BaseChartAxis*> result;
  if (types.testFlag(BaseChartAxis::atLeft))
    result << addAxis(BaseChartAxis::atLeft);
  if (types.testFlag(BaseChartAxis::atRight))
    result << addAxis(BaseChartAxis::atRight);
  if (types.testFlag(BaseChartAxis::atTop))
    result << addAxis(BaseChartAxis::atTop);
  if (types.testFlag(BaseChartAxis::atBottom))
    result << addAxis(BaseChartAxis::atBottom);
  return result;
}

/*!
  Removes the specified \a axis from the axis rect and deletes it.

  Returns true on success, i.e. if \a axis was a valid axis in this axis rect.

  \see addAxis
*/
bool BaseChartAxisRect::removeAxis(BaseChartAxis* axis) {
  // don't access axis->axisType() to provide safety when axis is an invalid
  // pointer, rather go through all axis containers:
  QHashIterator<BaseChartAxis::AxisType, QList<BaseChartAxis*> > it(mAxes);
  while (it.hasNext()) {
    it.next();
    if (it.value().contains(axis)) {
      if (it.value().first() == axis &&
          it.value().size() > 1)  // if removing first axis, transfer axis
                                  // offset to the new first axis (which at this
                                  // point is the second axis, if it exists)
        it.value()[1]->setOffset(axis->offset());
      mAxes[it.key()].removeOne(axis);
      if (qobject_cast<BaseChartCustomPlot*>(
              parentPlot()))  // make sure this isn't called from QObject dtor
                              // when BaseChartCustomPlot is already destructed
                              // (happens when the axis rect is not in any
                              // layout and thus QObject-child of
                              // BaseChartCustomPlot)
        parentPlot()->axisRemoved(axis);
      delete axis;
      return true;
    }
  }
  qDebug() << Q_FUNC_INFO
           << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
  return false;
}

/*!
  Zooms in (or out) to the passed rectangular region \a pixelRect, given in
  pixel coordinates.

  All axes of this axis rect will have their range zoomed accordingly. If you
  only wish to zoom specific axes, use the overloaded version of this method.

  \see BaseChartCustomPlot::setSelectionRectMode
*/
void BaseChartAxisRect::zoom(const QRectF& pixelRect) {
  zoom(pixelRect, axes());
}

/*! \overload

  Zooms in (or out) to the passed rectangular region \a pixelRect, given in
  pixel coordinates.

  Only the axes passed in \a affectedAxes will have their ranges zoomed
  accordingly.

  \see BaseChartCustomPlot::setSelectionRectMode
*/
void BaseChartAxisRect::zoom(const QRectF& pixelRect,
                             const QList<BaseChartAxis*>& affectedAxes) {
  foreach (BaseChartAxis* axis, affectedAxes) {
    if (!axis) {
      qDebug() << Q_FUNC_INFO << "a passed axis was zero";
      continue;
    }
    BaseChartRange pixelRange;
    if (axis->orientation() == Qt::Horizontal)
      pixelRange = BaseChartRange(pixelRect.left(), pixelRect.right());
    else
      pixelRange = BaseChartRange(pixelRect.top(), pixelRect.bottom());
    axis->setRange(axis->pixelToCoord(pixelRange.lower),
                   axis->pixelToCoord(pixelRange.upper));
  }
}

/*!
  Convenience function to create an axis on each side that doesn't have any axes
  yet and set their visibility to true. Further, the top/right axes are assigned
  the following properties of the bottom/left axes:

  \li range (\ref BaseChartAxis::setRange)
  \li range reversed (\ref BaseChartAxis::setRangeReversed)
  \li scale type (\ref BaseChartAxis::setScaleType)
  \li tick visibility (\ref BaseChartAxis::setTicks)
  \li number format (\ref BaseChartAxis::setNumberFormat)
  \li number precision (\ref BaseChartAxis::setNumberPrecision)
  \li tick count of ticker (\ref QCPAxisTicker::setTickCount)
  \li tick origin of ticker (\ref QCPAxisTicker::setTickOrigin)

  Tick label visibility (\ref BaseChartAxis::setTickLabels) of the right and top
  axes are set to false.

  If \a connectRanges is true, the \ref BaseChartAxis::rangeChanged
  "rangeChanged" signals of the bottom and left axes are connected to the \ref
  BaseChartAxis::setRange slots of the top and right axes.
*/
void BaseChartAxisRect::setupFullAxesBox(bool connectRanges) {
  BaseChartAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
  if (axisCount(BaseChartAxis::atBottom) == 0)
    xAxis = addAxis(BaseChartAxis::atBottom);
  else
    xAxis = axis(BaseChartAxis::atBottom);

  if (axisCount(BaseChartAxis::atLeft) == 0)
    yAxis = addAxis(BaseChartAxis::atLeft);
  else
    yAxis = axis(BaseChartAxis::atLeft);

  if (axisCount(BaseChartAxis::atTop) == 0)
    xAxis2 = addAxis(BaseChartAxis::atTop);
  else
    xAxis2 = axis(BaseChartAxis::atTop);

  if (axisCount(BaseChartAxis::atRight) == 0)
    yAxis2 = addAxis(BaseChartAxis::atRight);
  else
    yAxis2 = axis(BaseChartAxis::atRight);

  xAxis->setVisible(true);
  yAxis->setVisible(true);
  xAxis2->setVisible(true);
  yAxis2->setVisible(true);
  xAxis2->setTickLabels(false);
  yAxis2->setTickLabels(false);

  xAxis2->setRange(xAxis->range());
  xAxis2->setRangeReversed(xAxis->rangeReversed());
  xAxis2->setScaleType(xAxis->scaleType());
  xAxis2->setTicks(xAxis->ticks());
  xAxis2->setNumberFormat(xAxis->numberFormat());
  xAxis2->setNumberPrecision(xAxis->numberPrecision());
  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());

  yAxis2->setRange(yAxis->range());
  yAxis2->setRangeReversed(yAxis->rangeReversed());
  yAxis2->setScaleType(yAxis->scaleType());
  yAxis2->setTicks(yAxis->ticks());
  yAxis2->setNumberFormat(yAxis->numberFormat());
  yAxis2->setNumberPrecision(yAxis->numberPrecision());
  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());

  if (connectRanges) {
    connect(xAxis, SIGNAL(rangeChanged(BaseChartRange)), xAxis2,
            SLOT(setRange(BaseChartRange)));
    connect(yAxis, SIGNAL(rangeChanged(BaseChartRange)), yAxis2,
            SLOT(setRange(BaseChartRange)));
  }
}

/*!
  Returns a list of all the plottables that are associated with this axis rect.

  A plottable is considered associated with an axis rect if its key or value
  axis (or both) is in this axis rect.

  \see graphs, items
*/
QList<BaseChartAbstractPlottable*> BaseChartAxisRect::plottables() const {
  // Note: don't append all BaseChartAxis::plottables() into a list, because we
  // might get duplicate entries
  QList<BaseChartAbstractPlottable*> result;
  foreach (BaseChartAbstractPlottable* plottable, mParentPlot->mPlottables) {
    if (plottable->keyAxis()->axisRect() == this ||
        plottable->valueAxis()->axisRect() == this)
      result.append(plottable);
  }
  return result;
}

/*!
  Returns a list of all the graphs that are associated with this axis rect.

  A graph is considered associated with an axis rect if its key or value axis
  (or both) is in this axis rect.

  \see plottables, items
*/
QList<BaseChartGraph*> BaseChartAxisRect::graphs() const {
  // Note: don't append all BaseChartAxis::graphs() into a list, because we
  // might get duplicate entries
  QList<BaseChartGraph*> result;
  foreach (BaseChartGraph* graph, mParentPlot->mGraphs) {
    if (graph->keyAxis()->axisRect() == this ||
        graph->valueAxis()->axisRect() == this)
      result.append(graph);
  }
  return result;
}

/*!
  Returns a list of all the items that are associated with this axis rect.

  An item is considered associated with an axis rect if any of its positions has
  key or value axis set to an axis that is in this axis rect, or if any of its
  positions has \ref BaseChartItemPosition::setAxisRect set to the axis rect, or
  if the clip axis rect (\ref BaseChartAbstractItem::setClipAxisRect) is set to
  this axis rect.

  \see plottables, graphs
*/
QList<BaseChartAbstractItem*> BaseChartAxisRect::items() const {
  // Note: don't just append all BaseChartAxis::items() into a list, because we
  // might get duplicate entries
  //       and miss those items that have this axis rect as clipAxisRect.
  QList<BaseChartAbstractItem*> result;
  foreach (BaseChartAbstractItem* item, mParentPlot->mItems) {
    if (item->clipAxisRect() == this) {
      result.append(item);
      continue;
    }
    foreach (BaseChartItemPosition* position, item->positions()) {
      if (position->axisRect() == this ||
          position->keyAxis()->axisRect() == this ||
          position->valueAxis()->axisRect() == this) {
        result.append(item);
        break;
      }
    }
  }
  return result;
}

/*!
  This method is called automatically upon replot and doesn't need to be called
  by users of BaseChartAxisRect.

  Calls the base class implementation to update the margins (see \ref
  BaseChartLayoutElement::update), and finally passes the \ref rect to the inset
  layout (\ref insetLayout) and calls its QCPInsetLayout::update function.

  \seebaseclassmethod
*/
void BaseChartAxisRect::update(UpdatePhase phase) {
  BaseChartLayoutElement::update(phase);

  switch (phase) {
    case upPreparation: {
      foreach (BaseChartAxis* axis, axes()) axis->setupTickVectors();
      break;
    }
    case upLayout: {
      mInsetLayout->setOuterRect(rect());
      break;
    }
    default:
      break;
  }

  // pass update call on to inset layout (doesn't happen automatically, because
  // BaseChartAxisRect doesn't derive from QCPLayout):
  mInsetLayout->update(phase);
}

/* inherits documentation from base class */
QList<BaseChartLayoutElement*> BaseChartAxisRect::elements(
    bool recursive) const {
  QList<BaseChartLayoutElement*> result;
  if (mInsetLayout) {
    result << mInsetLayout;
    if (recursive) result << mInsetLayout->elements(recursive);
  }
  return result;
}

/* inherits documentation from base class */
void BaseChartAxisRect::applyDefaultAntialiasingHint(
    BaseChartPainter* painter) const {
  painter->setAntialiasing(false);
}

/* inherits documentation from base class */
void BaseChartAxisRect::draw(BaseChartPainter* painter) {
  drawBackground(painter);
}

/*!
  Sets \a pm as the axis background pixmap. The axis background pixmap will be
  drawn inside the axis rect. Since axis rects place themselves on the
  "background" layer by default, the axis rect backgrounds are usually drawn
  below everything else.

  For cases where the provided pixmap doesn't have the same size as the axis
  rect, scaling can be enabled with \ref setBackgroundScaled and the scaling
  mode (i.e. whether and how the aspect ratio is preserved) can be set with \ref
  setBackgroundScaledMode. To set all these options in one call, consider using
  the overloaded version of this function.

  Below the pixmap, the axis rect may be optionally filled with a brush, if
  specified with \ref setBackground(const QBrush &brush).

  \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush
  &brush)
*/
void BaseChartAxisRect::setBackground(const QPixmap& pm) {
  mBackgroundPixmap = pm;
  mScaledBackgroundPixmap = QPixmap();
}

/*! \overload

  Sets \a brush as the background brush. The axis rect background will be filled
  with this brush. Since axis rects place themselves on the "background" layer
  by default, the axis rect backgrounds are usually drawn below everything else.

  The brush will be drawn before (under) any background pixmap, which may be
  specified with \ref setBackground(const QPixmap &pm).

  To disable drawing of a background brush, set \a brush to Qt::NoBrush.

  \see setBackground(const QPixmap &pm)
*/
void BaseChartAxisRect::setBackground(const QBrush& brush) {
  mBackgroundBrush = brush;
}

/*! \overload

  Allows setting the background pixmap of the axis rect, whether it shall be
  scaled and how it shall be scaled in one call.

  \see setBackground(const QPixmap &pm), setBackgroundScaled,
  setBackgroundScaledMode
*/
void BaseChartAxisRect::setBackground(const QPixmap& pm, bool scaled,
                                      Qt::AspectRatioMode mode) {
  mBackgroundPixmap = pm;
  mScaledBackgroundPixmap = QPixmap();
  mBackgroundScaled = scaled;
  mBackgroundScaledMode = mode;
}

/*!
  Sets whether the axis background pixmap shall be scaled to fit the axis rect
  or not. If \a scaled is set to true, you may control whether and how the
  aspect ratio of the original pixmap is preserved with \ref
  setBackgroundScaledMode.

  Note that the scaled version of the original pixmap is buffered, so there is
  no performance penalty on replots. (Except when the axis rect dimensions are
  changed continuously.)

  \see setBackground, setBackgroundScaledMode
*/
void BaseChartAxisRect::setBackgroundScaled(bool scaled) {
  mBackgroundScaled = scaled;
}

/*!
  If scaling of the axis background pixmap is enabled (\ref
  setBackgroundScaled), use this function to define whether and how the aspect
  ratio of the original pixmap passed to \ref setBackground is preserved. \see
  setBackground, setBackgroundScaled
*/
void BaseChartAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
  mBackgroundScaledMode = mode;
}

/*!
  Returns the range drag axis of the \a orientation provided. If multiple axes
  were set, returns the first one (use \ref rangeDragAxes to retrieve a list
  with all set axes).

  \see setRangeDragAxes
*/
BaseChartAxis* BaseChartAxisRect::rangeDragAxis(Qt::Orientation orientation) {
  if (orientation == Qt::Horizontal)
    return mRangeDragHorzAxis.isEmpty() ? nullptr
                                        : mRangeDragHorzAxis.first().data();
  else
    return mRangeDragVertAxis.isEmpty() ? nullptr
                                        : mRangeDragVertAxis.first().data();
}

/*!
  Returns the range zoom axis of the \a orientation provided. If multiple axes
  were set, returns the first one (use \ref rangeZoomAxes to retrieve a list
  with all set axes).

  \see setRangeZoomAxes
*/
BaseChartAxis* BaseChartAxisRect::rangeZoomAxis(Qt::Orientation orientation) {
  if (orientation == Qt::Horizontal)
    return mRangeZoomHorzAxis.isEmpty() ? nullptr
                                        : mRangeZoomHorzAxis.first().data();
  else
    return mRangeZoomVertAxis.isEmpty() ? nullptr
                                        : mRangeZoomVertAxis.first().data();
}

/*!
  Returns all range drag axes of the \a orientation provided.

  \see rangeZoomAxis, setRangeZoomAxes
*/
QList<BaseChartAxis*> BaseChartAxisRect::rangeDragAxes(
    Qt::Orientation orientation) {
  QList<BaseChartAxis*> result;
  if (orientation == Qt::Horizontal) {
    foreach (QPointer<BaseChartAxis> axis, mRangeDragHorzAxis) {
      if (!axis.isNull()) result.append(axis.data());
    }
  } else {
    foreach (QPointer<BaseChartAxis> axis, mRangeDragVertAxis) {
      if (!axis.isNull()) result.append(axis.data());
    }
  }
  return result;
}

/*!
  Returns all range zoom axes of the \a orientation provided.

  \see rangeDragAxis, setRangeDragAxes
*/
QList<BaseChartAxis*> BaseChartAxisRect::rangeZoomAxes(
    Qt::Orientation orientation) {
  QList<BaseChartAxis*> result;
  if (orientation == Qt::Horizontal) {
    foreach (QPointer<BaseChartAxis> axis, mRangeZoomHorzAxis) {
      if (!axis.isNull()) result.append(axis.data());
    }
  } else {
    foreach (QPointer<BaseChartAxis> axis, mRangeZoomVertAxis) {
      if (!axis.isNull()) result.append(axis.data());
    }
  }
  return result;
}

/*!
  Returns the range zoom factor of the \a orientation provided.

  \see setRangeZoomFactor
*/
double BaseChartAxisRect::rangeZoomFactor(Qt::Orientation orientation) {
  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz
                                        : mRangeZoomFactorVert);
}

/*!
  Sets which axis orientation may be range dragged by the user with mouse
  interaction. What orientation corresponds to which specific axis can be set
  with \ref setRangeDragAxes(BaseChartAxis *horizontal, BaseChartAxis
  *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the
  vertical axis is the left axis (yAxis).

  To disable range dragging entirely, pass \c nullptr as \a orientations or
  remove \ref iRangeDrag from \ref BaseChartCustomPlot::setInteractions. To
  enable range dragging for both directions, pass <tt>Qt::Horizontal |
  Qt::Vertical</tt> as \a orientations.

  In addition to setting \a orientations to a non-zero value, make sure \ref
  BaseChartCustomPlot::setInteractions contains \ref iRangeDrag to enable the
  range dragging interaction.

  \see setRangeZoom, setRangeDragAxes,
  BaseChartCustomPlot::setNoAntialiasingOnDrag
*/
void BaseChartAxisRect::setRangeDrag(Qt::Orientations orientations) {
  mRangeDrag = orientations;
}

/*!
  Sets which axis orientation may be zoomed by the user with the mouse wheel.
  What orientation corresponds to which specific axis can be set with \ref
  setRangeZoomAxes(BaseChartAxis *horizontal, BaseChartAxis *vertical). By
  default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
  is the left axis (yAxis).

  To disable range zooming entirely, pass \c nullptr as \a orientations or
  remove \ref iRangeZoom from \ref BaseChartCustomPlot::setInteractions. To
  enable range zooming for both directions, pass <tt>Qt::Horizontal |
  Qt::Vertical</tt> as \a orientations.

  In addition to setting \a orientations to a non-zero value, make sure \ref
  BaseChartCustomPlot::setInteractions contains \ref iRangeZoom to enable the
  range zooming interaction.

  \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
*/
void BaseChartAxisRect::setRangeZoom(Qt::Orientations orientations) {
  mRangeZoom = orientations;
}

/*! \overload

  Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse
  range dragging on the BaseChartCustomPlot widget. Pass \c nullptr if no axis
  shall be dragged in the respective orientation.

  Use the overload taking a list of axes, if multiple axes (more than one per
  orientation) shall react to dragging interactions.

  \see setRangeZoomAxes
*/
void BaseChartAxisRect::setRangeDragAxes(BaseChartAxis* horizontal,
                                         BaseChartAxis* vertical) {
  QList<BaseChartAxis*> horz, vert;
  if (horizontal) horz.append(horizontal);
  if (vertical) vert.append(vertical);
  setRangeDragAxes(horz, vert);
}

/*! \overload

  This method allows to set up multiple axes to react to horizontal and vertical
  dragging. The drag orientation that the respective axis will react to is
  deduced from its orientation (\ref BaseChartAxis::orientation).

  In the unusual case that you wish to e.g. drag a vertically oriented axis with
  a horizontal drag motion, use the overload taking two separate lists for
  horizontal and vertical dragging.
*/
void BaseChartAxisRect::setRangeDragAxes(QList<BaseChartAxis*> axes) {
  QList<BaseChartAxis*> horz, vert;
  foreach (BaseChartAxis* ax, axes) {
    if (ax->orientation() == Qt::Horizontal)
      horz.append(ax);
    else
      vert.append(ax);
  }
  setRangeDragAxes(horz, vert);
}

/*! \overload

  This method allows to set multiple axes up to react to horizontal and vertical
  dragging, and define specifically which axis reacts to which drag orientation
  (irrespective of the axis orientation).
*/
void BaseChartAxisRect::setRangeDragAxes(QList<BaseChartAxis*> horizontal,
                                         QList<BaseChartAxis*> vertical) {
  mRangeDragHorzAxis.clear();
  foreach (BaseChartAxis* ax, horizontal) {
    QPointer<BaseChartAxis> axPointer(ax);
    if (!axPointer.isNull())
      mRangeDragHorzAxis.append(axPointer);
    else
      qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:"
               << reinterpret_cast<quintptr>(ax);
  }
  mRangeDragVertAxis.clear();
  foreach (BaseChartAxis* ax, vertical) {
    QPointer<BaseChartAxis> axPointer(ax);
    if (!axPointer.isNull())
      mRangeDragVertAxis.append(axPointer);
    else
      qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:"
               << reinterpret_cast<quintptr>(ax);
  }
}

/*!
  Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse
  wheel zooming on the BaseChartCustomPlot widget. Pass \c nullptr if no axis
  shall be zoomed in the respective orientation.

  The two axes can be zoomed with different strengths, when different factors
  are passed to \ref setRangeZoomFactor(double horizontalFactor, double
  verticalFactor).

  Use the overload taking a list of axes, if multiple axes (more than one per
  orientation) shall react to zooming interactions.

  \see setRangeDragAxes
*/
void BaseChartAxisRect::setRangeZoomAxes(BaseChartAxis* horizontal,
                                         BaseChartAxis* vertical) {
  QList<BaseChartAxis*> horz, vert;
  if (horizontal) horz.append(horizontal);
  if (vertical) vert.append(vertical);
  setRangeZoomAxes(horz, vert);
}

/*! \overload

  This method allows to set up multiple axes to react to horizontal and vertical
  range zooming. The zoom orientation that the respective axis will react to is
  deduced from its orientation (\ref BaseChartAxis::orientation).

  In the unusual case that you wish to e.g. zoom a vertically oriented axis with
  a horizontal zoom interaction, use the overload taking two separate lists for
  horizontal and vertical zooming.
*/
void BaseChartAxisRect::setRangeZoomAxes(QList<BaseChartAxis*> axes) {
  QList<BaseChartAxis*> horz, vert;
  foreach (BaseChartAxis* ax, axes) {
    if (ax->orientation() == Qt::Horizontal)
      horz.append(ax);
    else
      vert.append(ax);
  }
  setRangeZoomAxes(horz, vert);
}

/*! \overload

  This method allows to set multiple axes up to react to horizontal and vertical
  zooming, and define specifically which axis reacts to which zoom orientation
  (irrespective of the axis orientation).
*/
void BaseChartAxisRect::setRangeZoomAxes(QList<BaseChartAxis*> horizontal,
                                         QList<BaseChartAxis*> vertical) {
  mRangeZoomHorzAxis.clear();
  foreach (BaseChartAxis* ax, horizontal) {
    QPointer<BaseChartAxis> axPointer(ax);
    if (!axPointer.isNull())
      mRangeZoomHorzAxis.append(axPointer);
    else
      qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:"
               << reinterpret_cast<quintptr>(ax);
  }
  mRangeZoomVertAxis.clear();
  foreach (BaseChartAxis* ax, vertical) {
    QPointer<BaseChartAxis> axPointer(ax);
    if (!axPointer.isNull())
      mRangeZoomVertAxis.append(axPointer);
    else
      qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:"
               << reinterpret_cast<quintptr>(ax);
  }
}

/*!
  Sets how strong one rotation step of the mouse wheel zooms, when range zoom
  was activated with \ref setRangeZoom. The two parameters \a horizontalFactor
  and \a verticalFactor provide a way to let the horizontal axis zoom at
  different rates than the vertical axis. Which axis is horizontal and which is
  vertical, can be set with \ref setRangeZoomAxes.

  When the zoom factor is greater than one, scrolling the mouse wheel backwards
  (towards the user) will zoom in (make the currently visible range smaller).
  For zoom factors smaller than one, the same scrolling direction will zoom out.
*/
void BaseChartAxisRect::setRangeZoomFactor(double horizontalFactor,
                                           double verticalFactor) {
  mRangeZoomFactorHorz = horizontalFactor;
  mRangeZoomFactorVert = verticalFactor;
}

/*! \overload

  Sets both the horizontal and vertical zoom \a factor.
*/
void BaseChartAxisRect::setRangeZoomFactor(double factor) {
  mRangeZoomFactorHorz = factor;
  mRangeZoomFactorVert = factor;
}

/*! \internal

  Draws the background of this axis rect. It may consist of a background fill (a
  QBrush) and a pixmap.

  If a brush was given via \ref setBackground(const QBrush &brush), this
  function first draws an according filling inside the axis rect with the
  provided \a painter.

  Then, if a pixmap was provided via \ref setBackground, this function buffers
  the scaled version depending on \ref setBackgroundScaled and \ref
  setBackgroundScaledMode and then draws it inside the axis rect with the
  provided \a painter. The scaled version is buffered in mScaledBackgroundPixmap
  to prevent expensive rescaling at every redraw. It is only updated, when the
  axis rect has changed in a way that requires a rescale of the background
  pixmap (this is dependent on the \ref setBackgroundScaledMode), or when a
  differend axis background pixmap was set.

  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
*/
void BaseChartAxisRect::drawBackground(BaseChartPainter* painter) {
  // draw background fill:
  if (mBackgroundBrush != Qt::NoBrush)
    painter->fillRect(mRect, mBackgroundBrush);

  // draw background pixmap (on top of fill, if brush specified):
  if (!mBackgroundPixmap.isNull()) {
    if (mBackgroundScaled) {
      // check whether mScaledBackground needs to be updated:
      QSize scaledSize(mBackgroundPixmap.size());
      scaledSize.scale(mRect.size(), mBackgroundScaledMode);
      if (mScaledBackgroundPixmap.size() != scaledSize)
        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(
            mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
      painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
                          mScaledBackgroundPixmap,
                          QRect(0, 0, mRect.width(), mRect.height()) &
                              mScaledBackgroundPixmap.rect());
    } else {
      painter->drawPixmap(mRect.topLeft() + QPoint(0, -1), mBackgroundPixmap,
                          QRect(0, 0, mRect.width(), mRect.height()));
    }
  }
}

/*! \internal

  This function makes sure multiple axes on the side specified with \a type
  don't collide, but are distributed according to their respective space
  requirement (BaseChartAxis::calculateMargin).

  It does this by setting an appropriate offset (\ref BaseChartAxis::setOffset)
  on all axes except the one with index zero.

  This function is called by \ref calculateAutoMargin.
*/
void BaseChartAxisRect::updateAxesOffset(BaseChartAxis::AxisType type) {
  const QList<BaseChartAxis*> axesList = mAxes.value(type);
  if (axesList.isEmpty()) return;

  bool isFirstVisible =
      !axesList.first()
           ->visible();  // if the first axis is visible, the second axis (which
                         // is where the loop starts) isn't the first visible
                         // axis, so initialize with false
  for (int i = 1; i < axesList.size(); ++i) {
    int offset =
        axesList.at(i - 1)->offset() + axesList.at(i - 1)->calculateMargin();
    if (axesList.at(i)
            ->visible())  // only add inner tick length to offset if this axis
                          // is visible and it's not the first visible one
                          // (might happen if true first axis is invisible)
    {
      if (!isFirstVisible) offset += axesList.at(i)->tickLengthIn();
      isFirstVisible = false;
    }
    axesList.at(i)->setOffset(offset);
  }
}

/* inherits documentation from base class */
int BaseChartAxisRect::calculateAutoMargin(MarginSide side) {
  if (!mAutoMargins.testFlag(side))
    qDebug() << Q_FUNC_INFO
             << "Called with side that isn't specified as auto margin";

  updateAxesOffset(BaseChartAxis::marginSideToAxisType(side));

  // note: only need to look at the last (outer most) axis to determine the
  // total margin, due to updateAxisOffset call
  const QList<BaseChartAxis*> axesList =
      mAxes.value(BaseChartAxis::marginSideToAxisType(side));
  if (!axesList.isEmpty())
    return axesList.last()->offset() + axesList.last()->calculateMargin();
  else
    return 0;
}

/*! \internal

  Reacts to a change in layout to potentially set the convenience axis pointers
  \ref BaseChartCustomPlot::xAxis, \ref BaseChartCustomPlot::yAxis, etc. of the
  parent BaseChartCustomPlot to the respective axes of this axis rect. This is
  only done if the respective convenience pointer is currently zero and if there
  is no BaseChartAxisRect at position (0, 0) of the plot layout.

  This automation makes it simpler to replace the main axis rect with a newly
  created one, without the need to manually reset the convenience pointers.
*/
void BaseChartAxisRect::layoutChanged() {
  if (mParentPlot && mParentPlot->axisRectCount() > 0 &&
      mParentPlot->axisRect(0) == this) {
    if (axisCount(BaseChartAxis::atBottom) > 0 && !mParentPlot->xAxis)
      mParentPlot->xAxis = axis(BaseChartAxis::atBottom);
    if (axisCount(BaseChartAxis::atLeft) > 0 && !mParentPlot->yAxis)
      mParentPlot->yAxis = axis(BaseChartAxis::atLeft);
    if (axisCount(BaseChartAxis::atTop) > 0 && !mParentPlot->xAxis2)
      mParentPlot->xAxis2 = axis(BaseChartAxis::atTop);
    if (axisCount(BaseChartAxis::atRight) > 0 && !mParentPlot->yAxis2)
      mParentPlot->yAxis2 = axis(BaseChartAxis::atRight);
  }
}

/*! \internal

  Event handler for when a mouse button is pressed on the axis rect. If the left
  mouse button is pressed, the range dragging interaction is initialized (the
  actual range manipulation happens in the \ref mouseMoveEvent).

  The mDragging flag is set to true and some anchor points are set that are
  needed to determine the distance the mouse was dragged in the mouse
  move/release events later.

  \see mouseMoveEvent, mouseReleaseEvent
*/
void BaseChartAxisRect::mousePressEvent(QMouseEvent* event,
                                        const QVariant& details) {
  Q_UNUSED(details)
  if (event->buttons() & Qt::LeftButton) {
    mDragging = true;
    // initialize antialiasing backup in case we start dragging:
    if (mParentPlot->noAntialiasingOnDrag()) {
      mAADragBackup = mParentPlot->antialiasedElements();
      mNotAADragBackup = mParentPlot->notAntialiasedElements();
    }
    // Mouse range dragging interaction:
    if (mParentPlot->interactions().testFlag(iRangeDrag)) {
      mDragStartHorzRange.clear();
      foreach (QPointer<BaseChartAxis> axis, mRangeDragHorzAxis)
        mDragStartHorzRange.append(axis.isNull() ? BaseChartRange()
                                                 : axis->range());
      mDragStartVertRange.clear();
      foreach (QPointer<BaseChartAxis> axis, mRangeDragVertAxis)
        mDragStartVertRange.append(axis.isNull() ? BaseChartRange()
                                                 : axis->range());
    }
  }
}

/*! \internal

  Event handler for when the mouse is moved on the axis rect. If range dragging
  was activated in a preceding \ref mousePressEvent, the range is moved
  accordingly.

  \see mousePressEvent, mouseReleaseEvent
*/
void BaseChartAxisRect::mouseMoveEvent(QMouseEvent* event,
                                       const QPointF& startPos) {
  Q_UNUSED(startPos)
  // Mouse range dragging interaction:
  if (mDragging && mParentPlot->interactions().testFlag(iRangeDrag)) {
    if (mRangeDrag.testFlag(Qt::Horizontal)) {
      for (int i = 0; i < mRangeDragHorzAxis.size(); ++i) {
        BaseChartAxis* ax = mRangeDragHorzAxis.at(i).data();
        if (!ax) continue;
        if (i >= mDragStartHorzRange.size()) break;
        if (ax->mScaleType == BaseChartAxis::stLinear) {
          double diff = ax->pixelToCoord(startPos.x()) -
                        ax->pixelToCoord(event->pos().x());
          ax->setRange(mDragStartHorzRange.at(i).lower + diff,
                       mDragStartHorzRange.at(i).upper + diff);
        } else if (ax->mScaleType == BaseChartAxis::stLogarithmic) {
          double diff = ax->pixelToCoord(startPos.x()) /
                        ax->pixelToCoord(event->pos().x());
          ax->setRange(mDragStartHorzRange.at(i).lower * diff,
                       mDragStartHorzRange.at(i).upper * diff);
        }
      }
    }

    if (mRangeDrag.testFlag(Qt::Vertical)) {
      for (int i = 0; i < mRangeDragVertAxis.size(); ++i) {
        BaseChartAxis* ax = mRangeDragVertAxis.at(i).data();
        if (!ax) continue;
        if (i >= mDragStartVertRange.size()) break;
        if (ax->mScaleType == BaseChartAxis::stLinear) {
          double diff = ax->pixelToCoord(startPos.y()) -
                        ax->pixelToCoord(event->pos().y());
          ax->setRange(mDragStartVertRange.at(i).lower + diff,
                       mDragStartVertRange.at(i).upper + diff);
        } else if (ax->mScaleType == BaseChartAxis::stLogarithmic) {
          double diff = ax->pixelToCoord(startPos.y()) /
                        ax->pixelToCoord(event->pos().y());
          ax->setRange(mDragStartVertRange.at(i).lower * diff,
                       mDragStartVertRange.at(i).upper * diff);
        }
      }
    }

    if (mRangeDrag !=
        0)  // if either vertical or horizontal drag was enabled, do a replot
    {
      if (mParentPlot->noAntialiasingOnDrag())
        mParentPlot->setNotAntialiasedElements(aeAll);
      mParentPlot->replot(BaseChartCustomPlot::rpQueuedReplot);
    }
  }
}

/* inherits documentation from base class */
void BaseChartAxisRect::mouseReleaseEvent(QMouseEvent* event,
                                          const QPointF& startPos) {
  Q_UNUSED(event)
  Q_UNUSED(startPos)
  mDragging = false;
  if (mParentPlot->noAntialiasingOnDrag()) {
    mParentPlot->setAntialiasedElements(mAADragBackup);
    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
  }
}

/*! \internal

  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal,
  Qt::Vertical or both, the ranges of the axes defined as rangeZoomHorzAxis and
  rangeZoomVertAxis are scaled. The center of the scaling operation is the
  current cursor position inside the axis rect. The scaling factor is dependent
  on the mouse wheel delta (which direction the wheel was rotated) to provide a
  natural zooming feel. The Strength of the zoom can be controlled via \ref
  setRangeZoomFactor.

  Note, that event->angleDelta() is usually +/-120 for single rotation steps.
  However, if the mouse wheel is turned rapidly, many steps may bunch up to one
  event, so the delta may then be multiples of 120. This is taken into account
  here, by calculating \a wheelSteps and using it as exponent of the range zoom
  factor. This takes care of the wheel direction automatically, by inverting the
  factor, when the wheel step is negative (f^-1 = 1/f).
*/
void BaseChartAxisRect::wheelEvent(QWheelEvent* event) {
  const double delta = event->angleDelta().y();
  const QPointF pos = event->position();

  // Mouse range zooming interaction:
  if (mParentPlot->interactions().testFlag(iRangeZoom)) {
    if (mRangeZoom != 0) {
      double factor;
      double wheelSteps =
          delta / 120.0;  // a single step delta is +/-120 usually
      if (mRangeZoom.testFlag(Qt::Horizontal)) {
        factor = qPow(mRangeZoomFactorHorz, wheelSteps);
        foreach (QPointer<BaseChartAxis> axis, mRangeZoomHorzAxis) {
          if (!axis.isNull())
            axis->scaleRange(factor, axis->pixelToCoord(pos.x()));
        }
      }
      if (mRangeZoom.testFlag(Qt::Vertical)) {
        factor = qPow(mRangeZoomFactorVert, wheelSteps);
        foreach (QPointer<BaseChartAxis> axis, mRangeZoomVertAxis) {
          if (!axis.isNull())
            axis->scaleRange(factor, axis->pixelToCoord(pos.y()));
        }
      }
      mParentPlot->replot();
    }
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartAxisPainterPrivate
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartAxisPainterPrivate

  \internal
  \brief (Private)

  This is a private class and not part of the public BaseChartCustomPlot
  interface.

  It is used by BaseChartAxis to do the low-level drawing of axis backbone, tick
  marks, tick labels and axis label. It also buffers the labels to reduce replot
  times. The parameters are configured by directly accessing the public member
  variables.
*/

/*!
  Constructs a BaseChartAxisPainterPrivate instance. Make sure to not create a
  new instance on every redraw, to utilize the caching mechanisms.
*/
BaseChartAxisPainterPrivate::BaseChartAxisPainterPrivate(
    BaseChartCustomPlot* parentPlot)
    : type(BaseChartAxis::atLeft),
      basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      lowerEnding(BaseChartLineEnding::esNone),
      upperEnding(BaseChartLineEnding::esNone),
      labelPadding(0),
      tickLabelPadding(0),
      tickLabelRotation(0),
      tickLabelSide(BaseChartAxis::lsOutside),
      substituteExponent(true),
      numberMultiplyCross(false),
      tickLengthIn(5),
      tickLengthOut(0),
      subTickLengthIn(2),
      subTickLengthOut(0),
      tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
      offset(0),
      abbreviateDecimalPowers(false),
      reversedEndings(false),
      customGrid(false),
      mParentPlot(parentPlot),
      mLabelCache(16)  // cache at most 16 (tick) labels
{}

BaseChartAxisPainterPrivate::~BaseChartAxisPainterPrivate() {}

/*! \internal

  Draws the axis with the specified \a painter.

  The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox,
  mLabelSelectionBox) are set here, too.
*/
void BaseChartAxisPainterPrivate::draw(BaseChartPainter* painter) {
  QByteArray newHash = generateLabelParameterHash();
  if (newHash != mLabelParameterHash) {
    mLabelCache.clear();
    mLabelParameterHash = newHash;
  }

  QPoint origin;
  switch (type) {
    case BaseChartAxis::atLeft:
      origin = axisRect.bottomLeft() + QPoint(-offset, 0);
      break;
    case BaseChartAxis::atRight:
      origin = axisRect.bottomRight() + QPoint(+offset, 0);
      break;
    case BaseChartAxis::atTop:
      origin = axisRect.topLeft() + QPoint(0, -offset);
      break;
    case BaseChartAxis::atBottom:
      origin = axisRect.bottomLeft() + QPoint(0, +offset);
      break;
  }

  double xCor = 0,
         yCor = 0;  // paint system correction, for pixel exact matches (affects
                    // baselines and ticks of top/right axes)
  switch (type) {
    case BaseChartAxis::atTop:
      yCor = -1;
      break;
    case BaseChartAxis::atRight:
      xCor = 1;
      break;
    default:
      break;
  }
  int margin = 0;
  // draw baseline:
  QLineF baseLine;
  painter->setPen(basePen);
  if (BaseChartAxis::orientation(type) == Qt::Horizontal)
    baseLine.setPoints(origin + QPointF(xCor, yCor),
                       origin + QPointF(axisRect.width() + xCor, yCor));
  else
    baseLine.setPoints(origin + QPointF(xCor, yCor),
                       origin + QPointF(xCor, -axisRect.height() + yCor));
  if (reversedEndings)
    baseLine = QLineF(baseLine.p2(),
                      baseLine.p1());  // won't make a difference for line
                                       // itself, but for line endings later

  if (!isCustomGrid()) {
    painter->drawLine(baseLine);
  }

  // draw ticks:
  if (!tickPositions.isEmpty()) {
    painter->setPen(tickPen);
    int tickDir =
        (type == BaseChartAxis::atBottom || type == BaseChartAxis::atRight)
            ? -1
            : 1;  // direction of ticks ("inward" is right for left axis and
                  // left for right axis)
    if (BaseChartAxis::orientation(type) == Qt::Horizontal) {
      if (!isCustomGrid()) {
        foreach (double tickPos, tickPositions)
          painter->drawLine(QLineF(
              tickPos + xCor, origin.y() - tickLengthOut * tickDir + yCor,
              tickPos + xCor, origin.y() + tickLengthIn * tickDir + yCor));
      }
    } else {
      if (!isCustomGrid()) {
        foreach (double tickPos, tickPositions)
          painter->drawLine(QLineF(
              origin.x() - tickLengthOut * tickDir + xCor, tickPos + yCor,
              origin.x() + tickLengthIn * tickDir + xCor, tickPos + yCor));
      }
    }
  }

  // draw subticks:
  if (!subTickPositions.isEmpty()) {
    painter->setPen(subTickPen);
    // direction of ticks ("inward" is right for left axis and left for right
    // axis)
    int tickDir =
        (type == BaseChartAxis::atBottom || type == BaseChartAxis::atRight) ? -1
                                                                            : 1;
    if (BaseChartAxis::orientation(type) == Qt::Horizontal) {
      if (!isCustomGrid()) {
        foreach (double subTickPos, subTickPositions)
          painter->drawLine(QLineF(
              subTickPos + xCor, origin.y() - subTickLengthOut * tickDir + yCor,
              subTickPos + xCor,
              origin.y() + subTickLengthIn * tickDir + yCor));
      }
    } else {
      if (!isCustomGrid()) {
        foreach (double subTickPos, subTickPositions)
          painter->drawLine(QLineF(
              origin.x() - subTickLengthOut * tickDir + xCor, subTickPos + yCor,
              origin.x() + subTickLengthIn * tickDir + xCor,
              subTickPos + yCor));
      }
    }
  }
  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));

  // draw axis base endings:
  bool antialiasingBackup = painter->antialiasing();
  painter->setAntialiasing(true);  // always want endings to be antialiased,
                                   // even if base and ticks themselves aren't
  painter->setBrush(QBrush(basePen.color()));
  BaseChartVector2D baseLineVector(baseLine.dx(), baseLine.dy());
  if (lowerEnding.style() != BaseChartLineEnding::esNone)
    lowerEnding.draw(painter,
                     BaseChartVector2D(baseLine.p1()) -
                         baseLineVector.normalized() *
                             lowerEnding.realLength() *
                             (lowerEnding.inverted() ? -1 : 1),
                     -baseLineVector);
  if (upperEnding.style() != BaseChartLineEnding::esNone)
    upperEnding.draw(painter,
                     BaseChartVector2D(baseLine.p2()) +
                         baseLineVector.normalized() *
                             upperEnding.realLength() *
                             (upperEnding.inverted() ? -1 : 1),
                     baseLineVector);
  painter->setAntialiasing(antialiasingBackup);

  // tick labels:
  QRect oldClipRect;
  if (tickLabelSide == BaseChartAxis::lsInside)  // if using inside labels, clip
                                                 // them to the axis rect
  {
    oldClipRect = painter->clipRegion().boundingRect();
    painter->setClipRect(axisRect);
  }
  QSize tickLabelsSize(
      0,
      0);  // size of largest tick label, for offset calculation of axis label
  if (!tickLabels.isEmpty()) {
    if (tickLabelSide == BaseChartAxis::lsOutside) margin += tickLabelPadding;
    painter->setFont(tickLabelFont);
    painter->setPen(QPen(tickLabelColor));
    const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
    int distanceToAxis = margin;
    if (tickLabelSide == BaseChartAxis::lsInside)
      distanceToAxis =
          -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
    for (int i = 0; i < maxLabelIndex; ++i)
      placeTickLabel(painter, tickPositions.at(i), distanceToAxis,
                     tickLabels.at(i), &tickLabelsSize);
    if (tickLabelSide == BaseChartAxis::lsOutside)
      margin += (BaseChartAxis::orientation(type) == Qt::Horizontal)
                    ? tickLabelsSize.height()
                    : tickLabelsSize.width();
  }
  if (tickLabelSide == BaseChartAxis::lsInside)
    painter->setClipRect(oldClipRect);

  // axis label:
  QRect labelBounds;
  if (!label.isEmpty()) {
    margin += labelPadding;
    painter->setFont(labelFont);
    painter->setPen(QPen(labelColor));
    labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0,
                                                      Qt::TextDontClip, label);
    if (type == BaseChartAxis::atLeft) {
      QTransform oldTransform = painter->transform();
      painter->translate((origin.x() - margin - labelBounds.height()),
                         origin.y());
      painter->rotate(-90);
      painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
                        Qt::TextDontClip | Qt::AlignCenter, label);
      painter->setTransform(oldTransform);
    } else if (type == BaseChartAxis::atRight) {
      QTransform oldTransform = painter->transform();
      painter->translate((origin.x() + margin + labelBounds.height()),
                         origin.y() - axisRect.height());
      painter->rotate(90);
      painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
                        Qt::TextDontClip | Qt::AlignCenter, label);
      painter->setTransform(oldTransform);
    } else if (type == BaseChartAxis::atTop)
      painter->drawText(origin.x(), origin.y() - margin - labelBounds.height(),
                        axisRect.width(), labelBounds.height(),
                        Qt::TextDontClip | Qt::AlignCenter, label);
    else if (type == BaseChartAxis::atBottom)
      painter->drawText(origin.x(), origin.y() + margin, axisRect.width(),
                        labelBounds.height(),
                        Qt::TextDontClip | Qt::AlignCenter, label);
  }

  // set selection boxes:
  int selectionTolerance = 0;
  if (mParentPlot)
    selectionTolerance = mParentPlot->selectionTolerance();
  else
    qDebug() << Q_FUNC_INFO << "mParentPlot is null";
  int selAxisOutSize =
      qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
  int selAxisInSize = selectionTolerance;
  int selTickLabelSize;
  int selTickLabelOffset;
  if (tickLabelSide == BaseChartAxis::lsOutside) {
    selTickLabelSize = (BaseChartAxis::orientation(type) == Qt::Horizontal
                            ? tickLabelsSize.height()
                            : tickLabelsSize.width());
    selTickLabelOffset =
        qMax(tickLengthOut, subTickLengthOut) + tickLabelPadding;
  } else {
    selTickLabelSize = -(BaseChartAxis::orientation(type) == Qt::Horizontal
                             ? tickLabelsSize.height()
                             : tickLabelsSize.width());
    selTickLabelOffset =
        -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
  }
  int selLabelSize = labelBounds.height();
  int selLabelOffset =
      qMax(tickLengthOut, subTickLengthOut) +
      (!tickLabels.isEmpty() && tickLabelSide == BaseChartAxis::lsOutside
           ? tickLabelPadding + selTickLabelSize
           : 0) +
      labelPadding;
  if (type == BaseChartAxis::atLeft) {
    mAxisSelectionBox.setCoords(origin.x() - selAxisOutSize, axisRect.top(),
                                origin.x() + selAxisInSize, axisRect.bottom());
    mTickLabelsSelectionBox.setCoords(
        origin.x() - selTickLabelOffset - selTickLabelSize, axisRect.top(),
        origin.x() - selTickLabelOffset, axisRect.bottom());
    mLabelSelectionBox.setCoords(origin.x() - selLabelOffset - selLabelSize,
                                 axisRect.top(), origin.x() - selLabelOffset,
                                 axisRect.bottom());
  } else if (type == BaseChartAxis::atRight) {
    mAxisSelectionBox.setCoords(origin.x() - selAxisInSize, axisRect.top(),
                                origin.x() + selAxisOutSize, axisRect.bottom());
    mTickLabelsSelectionBox.setCoords(
        origin.x() + selTickLabelOffset + selTickLabelSize, axisRect.top(),
        origin.x() + selTickLabelOffset, axisRect.bottom());
    mLabelSelectionBox.setCoords(origin.x() + selLabelOffset + selLabelSize,
                                 axisRect.top(), origin.x() + selLabelOffset,
                                 axisRect.bottom());
  } else if (type == BaseChartAxis::atTop) {
    mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisOutSize,
                                axisRect.right(), origin.y() + selAxisInSize);
    mTickLabelsSelectionBox.setCoords(
        axisRect.left(), origin.y() - selTickLabelOffset - selTickLabelSize,
        axisRect.right(), origin.y() - selTickLabelOffset);
    mLabelSelectionBox.setCoords(axisRect.left(),
                                 origin.y() - selLabelOffset - selLabelSize,
                                 axisRect.right(), origin.y() - selLabelOffset);
  } else if (type == BaseChartAxis::atBottom) {
    mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisInSize,
                                axisRect.right(), origin.y() + selAxisOutSize);
    mTickLabelsSelectionBox.setCoords(
        axisRect.left(), origin.y() + selTickLabelOffset + selTickLabelSize,
        axisRect.right(), origin.y() + selTickLabelOffset);
    mLabelSelectionBox.setCoords(axisRect.left(),
                                 origin.y() + selLabelOffset + selLabelSize,
                                 axisRect.right(), origin.y() + selLabelOffset);
  }
  mAxisSelectionBox = mAxisSelectionBox.normalized();
  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
  mLabelSelectionBox = mLabelSelectionBox.normalized();
  // draw hitboxes for debug purposes:
  // painter->setBrush(Qt::NoBrush);
  // painter->drawRects(QVector<QRect>() << mAxisSelectionBox <<
  // mTickLabelsSelectionBox << mLabelSelectionBox);
}

/*! \internal

  Returns the size ("margin" in BaseChartAxisRect context, so measured
  perpendicular to the axis backbone direction) needed to fit the axis.
*/
int BaseChartAxisPainterPrivate::size() {
  int result = 0;

  QByteArray newHash = generateLabelParameterHash();
  if (newHash != mLabelParameterHash) {
    mLabelCache.clear();
    mLabelParameterHash = newHash;
  }

  // get length of tick marks pointing outwards:
  if (!tickPositions.isEmpty())
    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));

  // calculate size of tick labels:
  if (tickLabelSide == BaseChartAxis::lsOutside) {
    QSize tickLabelsSize(0, 0);
    if (!tickLabels.isEmpty()) {
      foreach (const QString& tickLabel, tickLabels)
        getMaxTickLabelSize(tickLabelFont, tickLabel, &tickLabelsSize);
      result += BaseChartAxis::orientation(type) == Qt::Horizontal
                    ? tickLabelsSize.height()
                    : tickLabelsSize.width();
      if (result < 40 && BaseChartAxis::orientation(type) == Qt::Vertical)
        result = 40;
      //    result += tickLabelPadding;
    }
  }

  // calculate size of axis label (only height needed, because left/right labels
  // are rotated by 90 degrees):
  if (!label.isEmpty()) {
    QFontMetrics fontMetrics(labelFont);
    QRect bounds;
    bounds = fontMetrics.boundingRect(
        0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter,
        label);
    result += bounds.height() + labelPadding;
  }

  return result;
}

/*! \internal

  Clears the internal label cache. Upon the next \ref draw, all labels will be
  created new. This method is called automatically in \ref draw, if any
  parameters have changed that invalidate the cached labels, such as font,
  color, etc.
*/
void BaseChartAxisPainterPrivate::clearCache() { mLabelCache.clear(); }

void BaseChartAxisPainterPrivate::setCustomGrid() { customGrid = true; }

/*! \internal

  Returns a hash that allows uniquely identifying whether the label parameters
  have changed such that the cached labels must be refreshed (\ref clearCache).
  It is used in \ref draw. If the return value of this method hasn't changed
  since the last redraw, the respective label parameters haven't changed and
  cached labels may be used.
*/
QByteArray BaseChartAxisPainterPrivate::generateLabelParameterHash() const {
  QByteArray result;
  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
  result.append(QByteArray::number(tickLabelRotation));
  result.append(QByteArray::number(int(tickLabelSide)));
  result.append(QByteArray::number(int(substituteExponent)));
  result.append(QByteArray::number(int(numberMultiplyCross)));
  result.append(tickLabelColor.name().toLatin1() +
                QByteArray::number(tickLabelColor.alpha(), 16));
  result.append(tickLabelFont.toString().toLatin1());
  return result;
}

/*! \internal

  Draws a single tick label with the provided \a painter, utilizing the internal
  label cache to significantly speed up drawing of labels that were drawn in
  previous calls. The tick label is always bound to an axis, the distance to the
  axis is controllable via \a distanceToAxis in pixels. The pixel position in
  the axis direction is passed in the \a position parameter. Hence for the
  bottom axis, \a position would indicate the horizontal pixel position (not
  coordinate), at which the label should be drawn.

  In order to later draw the axis label in a place that doesn't overlap with the
  tick labels, the largest tick label size is needed. This is acquired by
  passing a \a tickLabelsSize to the \ref drawTickLabel calls during the process
  of drawing all tick labels of one axis. In every call, \a tickLabelsSize is
  expanded, if the drawn label exceeds the value \a tickLabelsSize currently
  holds.

  The label is drawn with the font and pen that are currently set on the \a
  painter. To draw superscripted powers, the font is temporarily made smaller by
  a fixed factor (see \ref getTickLabelData).
*/
void BaseChartAxisPainterPrivate::placeTickLabel(BaseChartPainter* painter,
                                                 double position,
                                                 int distanceToAxis,
                                                 const QString& text,
                                                 QSize* tickLabelsSize) {
  // warning: if you change anything here, also adapt getMaxTickLabelSize()
  // accordingly!
  if (text.isEmpty()) return;
  QSize finalSize;
  QPointF labelAnchor;
  switch (type) {
    case BaseChartAxis::atLeft:
      labelAnchor =
          QPointF(axisRect.left() - distanceToAxis - offset, position);
      break;
    case BaseChartAxis::atRight:
      labelAnchor =
          QPointF(axisRect.right() + distanceToAxis + offset, position);
      break;
    case BaseChartAxis::atTop:
      labelAnchor = QPointF(position, axisRect.top() - distanceToAxis - offset);
      break;
    case BaseChartAxis::atBottom:
      labelAnchor =
          QPointF(position, axisRect.bottom() + distanceToAxis + offset);
      break;
  }
  if (mParentPlot->plottingHints().testFlag(phCacheLabels) &&
      !painter->modes().testFlag(
          BaseChartPainter::pmNoCaching))  // label caching enabled
  {
    CachedLabel* cachedLabel =
        mLabelCache.take(text);  // attempt to get label from cache
    if (!cachedLabel)            // no cached label existed, create it
    {
      cachedLabel = new CachedLabel;
      TickLabelData labelData = getTickLabelData(painter->font(), text);
      cachedLabel->offset = getTickLabelDrawOffset(labelData) +
                            labelData.rotatedTotalBounds.topLeft();
      if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio())) {
        cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size() *
                                      mParentPlot->bufferDevicePixelRatio());
        cachedLabel->pixmap.setDevicePixelRatio(
            mParentPlot->devicePixelRatioF());
      } else
        cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
      cachedLabel->pixmap.fill(Qt::transparent);
      BaseChartPainter cachePainter(&cachedLabel->pixmap);
      cachePainter.setPen(painter->pen());
      drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(),
                    -labelData.rotatedTotalBounds.topLeft().y(), labelData);
    }
    // if label would be partly clipped by widget border on sides, don't draw it
    // (only for outside tick labels):
    bool labelClippedByBorder = false;
    if (tickLabelSide == BaseChartAxis::lsOutside) {
      if (BaseChartAxis::orientation(type) == Qt::Horizontal)
        labelClippedByBorder =
            labelAnchor.x() + cachedLabel->offset.x() +
                    cachedLabel->pixmap.width() /
                        mParentPlot->bufferDevicePixelRatio() >
                viewportRect.right() ||
            labelAnchor.x() + cachedLabel->offset.x() < viewportRect.left();
      else
        labelClippedByBorder =
            labelAnchor.y() + cachedLabel->offset.y() +
                    cachedLabel->pixmap.height() /
                        mParentPlot->bufferDevicePixelRatio() >
                viewportRect.bottom() ||
            labelAnchor.y() + cachedLabel->offset.y() < viewportRect.top();
    }
    if (!labelClippedByBorder) {
      painter->drawPixmap(labelAnchor + cachedLabel->offset,
                          cachedLabel->pixmap);
      finalSize =
          cachedLabel->pixmap.size() / mParentPlot->bufferDevicePixelRatio();
    }
    mLabelCache.insert(text,
                       cachedLabel);  // return label to cache or insert for the
                                      // first time if newly created
  } else  // label caching disabled, draw text directly on surface:
  {
    TickLabelData labelData = getTickLabelData(painter->font(), text);
    QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
    // if label would be partly clipped by widget border on sides, don't draw it
    // (only for outside tick labels):
    bool labelClippedByBorder = false;
    if (tickLabelSide == BaseChartAxis::lsOutside) {
      if (BaseChartAxis::orientation(type) == Qt::Horizontal)
        labelClippedByBorder =
            finalPosition.x() + (labelData.rotatedTotalBounds.width() +
                                 labelData.rotatedTotalBounds.left()) >
                viewportRect.right() ||
            finalPosition.x() + labelData.rotatedTotalBounds.left() <
                viewportRect.left();
      else
        labelClippedByBorder =
            finalPosition.y() + (labelData.rotatedTotalBounds.height() +
                                 labelData.rotatedTotalBounds.top()) >
                viewportRect.bottom() ||
            finalPosition.y() + labelData.rotatedTotalBounds.top() <
                viewportRect.top();
    }
    if (!labelClippedByBorder) {
      drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
      finalSize = labelData.rotatedTotalBounds.size();
    }
  }

  // expand passed tickLabelsSize if current tick label is larger:
  if (finalSize.width() > tickLabelsSize->width())
    tickLabelsSize->setWidth(finalSize.width());
  if (finalSize.height() > tickLabelsSize->height())
    tickLabelsSize->setHeight(finalSize.height());
}

/*! \internal

  This is a \ref placeTickLabel helper function.

  Draws the tick label specified in \a labelData with \a painter at the pixel
  positions \a x and \a y. This function is used by \ref placeTickLabel to
  create new tick labels for the cache, or to directly draw the labels on the
  BaseChartCustomPlot surface when label caching is disabled, i.e. when
  phCacheLabels plotting hint is not set.
*/
void BaseChartAxisPainterPrivate::drawTickLabel(
    BaseChartPainter* painter, double x, double y,
    const TickLabelData& labelData) const {
  if (isCustomGrid()) {
    return;
  }

  // backup painter settings that we're about to change:
  QTransform oldTransform = painter->transform();
  QFont oldFont = painter->font();

  // transform painter to position/rotation:
  painter->translate(x, y);
  if (!qFuzzyIsNull(tickLabelRotation)) painter->rotate(tickLabelRotation);

  // draw text:
  if (!labelData.expPart
           .isEmpty())  // indicator that beautiful powers must be used
  {
    painter->setFont(labelData.baseFont);
    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
    if (!labelData.suffixPart.isEmpty())
      painter->drawText(
          labelData.baseBounds.width() + 1 + labelData.expBounds.width(), 0, 0,
          0, Qt::TextDontClip, labelData.suffixPart);
    painter->setFont(labelData.expFont);
    painter->drawText(labelData.baseBounds.width() + 1, 0,
                      labelData.expBounds.width(), labelData.expBounds.height(),
                      Qt::TextDontClip, labelData.expPart);
  } else {
    painter->setFont(labelData.baseFont);
    painter->drawText(0, 0, labelData.totalBounds.width(),
                      labelData.totalBounds.height(),
                      Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
  }

  // reset painter settings to what it was before:
  painter->setTransform(oldTransform);
  painter->setFont(oldFont);
}

/*! \internal

  This is a \ref placeTickLabel helper function.

  Transforms the passed \a text and \a font to a tickLabelData structure that
  can then be further processed by \ref getTickLabelDrawOffset and \ref
  drawTickLabel. It splits the text into base and exponent if necessary (member
  substituteExponent) and calculates appropriate bounding boxes.
*/
BaseChartAxisPainterPrivate::TickLabelData
BaseChartAxisPainterPrivate::getTickLabelData(const QFont& font,
                                              const QString& text) const {
  TickLabelData result;

  // determine whether beautiful decimal powers should be used
  bool useBeautifulPowers = false;
  int ePos = -1;   // first index of exponent part, text before that will be
                   // basePart, text until eLast will be expPart
  int eLast = -1;  // last index of exponent part, rest of text after this will
                   // be suffixPart
  if (substituteExponent) {
    ePos = text.indexOf(QLatin1Char('e'));
    if (ePos > 0 && text.at(ePos - 1).isDigit()) {
      eLast = ePos;
      while (eLast + 1 < text.size() &&
             (text.at(eLast + 1) == QLatin1Char('+') ||
              text.at(eLast + 1) == QLatin1Char('-') ||
              text.at(eLast + 1).isDigit()))
        ++eLast;
      if (eLast > ePos)  // only if also to right of 'e' is a digit/+/-
                         // interpret it as beautifiable power
        useBeautifulPowers = true;
    }
  }

  // calculate text bounding rects and do string preparation for beautiful
  // decimal powers:
  result.baseFont = font;
  if (result.baseFont.pointSizeF() >
      0)  // might return -1 if specified with setPixelSize, in that case we
          // can't do correction in next line
    result.baseFont.setPointSizeF(
        result.baseFont.pointSizeF() +
        0.05);  // QFontMetrics.boundingRect has a bug for exact point sizes
                // that make the results oscillate due to internal rounding
  if (useBeautifulPowers) {
    // split text into parts of number/symbol that will be drawn normally and
    // part that will be drawn as exponent:
    result.basePart = text.left(ePos);
    result.suffixPart =
        text.mid(eLast + 1);  // also drawn normally but after exponent
    // in log scaling, we want to turn "1*10^n" into "10^n", else add
    // multiplication sign and decimal base:
    if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
      result.basePart = QLatin1String("10");
    else
      result.basePart +=
          (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) +
          QLatin1String("10");
    result.expPart = text.mid(ePos + 1, eLast - ePos);
    // clip "+" and leading zeros off expPart:
    while (result.expPart.length() > 2 &&
           result.expPart.at(1) ==
               QLatin1Char('0'))  // length > 2 so we leave one zero when
                                  // numberFormatChar is 'e'
      result.expPart.remove(1, 1);
    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
      result.expPart.remove(0, 1);
    // prepare smaller font for exponent:
    result.expFont = font;
    if (result.expFont.pointSize() > 0)
      result.expFont.setPointSize(int(result.expFont.pointSize() * 0.75));
    else
      result.expFont.setPixelSize(int(result.expFont.pixelSize() * 0.75));
    // calculate bounding rects of base part(s), exponent part and total one:
    result.baseBounds =
        QFontMetrics(result.baseFont)
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
    result.expBounds =
        QFontMetrics(result.expFont)
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
    if (!result.suffixPart.isEmpty())
      result.suffixBounds =
          QFontMetrics(result.baseFont)
              .boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
    result.totalBounds = result.baseBounds.adjusted(
        0, 0, result.expBounds.width() + result.suffixBounds.width() + 2,
        0);  // +2 consists of the 1 pixel spacing between base and exponent
             // (see drawTickLabel) and an extra pixel to include AA
  } else     // useBeautifulPowers == false
  {
    result.basePart = text;
    result.totalBounds =
        QFontMetrics(result.baseFont)
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter,
                          result.basePart);
  }
  result.totalBounds.moveTopLeft(QPoint(
      0, 0));  // want bounding box aligned top left at origin, independent of
               // how it was created, to make further processing simpler

  // calculate possibly different bounding rect after rotation:
  result.rotatedTotalBounds = result.totalBounds;
  if (!qFuzzyIsNull(tickLabelRotation)) {
    QTransform transform;
    transform.rotate(tickLabelRotation);
    result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
  }

  return result;
}

/*! \internal

  This is a \ref placeTickLabel helper function.

  Calculates the offset at which the top left corner of the specified tick label
  shall be drawn. The offset is relative to a point right next to the tick the
  label belongs to.

  This function is thus responsible for e.g. centering tick labels under ticks
  and positioning them appropriately when they are rotated.
*/
QPointF BaseChartAxisPainterPrivate::getTickLabelDrawOffset(
    const TickLabelData& labelData) const {
  /*
    calculate label offset from base point at tick (non-trivial, for best visual
    appearance): short explanation for bottom axis: The anchor, i.e. the point
    in the label that is placed horizontally under the corresponding tick is
    always on the label side that is closer to the axis (e.g. the left side of
    the text when we're rotating clockwise). On that side, the height is halved
    and the resulting point is defined the anchor. This way, a 90 degree rotated
    text will be centered under the tick (i.e. displaced horizontally by half
    its height). At the same time, a 45 degree rotated text will "point toward"
    its tick, as is typical for rotated tick labels.
  */
  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
  bool flip =
      qFuzzyCompare(qAbs(tickLabelRotation),
                    90.0);  // perfect +/-90 degree flip. Indicates vertical
                            // label centering on vertical axes.
  double radians = tickLabelRotation / 180.0 * M_PI;
  double x = 0;
  double y = 0;
  if ((type == BaseChartAxis::atLeft &&
       tickLabelSide == BaseChartAxis::lsOutside) ||
      (type == BaseChartAxis::atRight &&
       tickLabelSide ==
           BaseChartAxis::lsInside))  // Anchor at right side of tick label
  {
    if (doRotation) {
      if (tickLabelRotation > 0) {
        x = -qCos(radians) * labelData.totalBounds.width();
        y = flip ? -labelData.totalBounds.width() / 2.0
                 : -qSin(radians) * labelData.totalBounds.width() -
                       qCos(radians) * labelData.totalBounds.height() / 2.0;
      } else {
        x = -qCos(-radians) * labelData.totalBounds.width() -
            qSin(-radians) * labelData.totalBounds.height();
        y = flip ? +labelData.totalBounds.width() / 2.0
                 : +qSin(-radians) * labelData.totalBounds.width() -
                       qCos(-radians) * labelData.totalBounds.height() / 2.0;
      }
    } else {
      x = -labelData.totalBounds.width();
      y = -labelData.totalBounds.height() / 2.0;
    }
  } else if ((type == BaseChartAxis::atRight &&
              tickLabelSide == BaseChartAxis::lsOutside) ||
             (type == BaseChartAxis::atLeft &&
              tickLabelSide == BaseChartAxis::lsInside))  // Anchor at left side
                                                          // of tick label
  {
    if (doRotation) {
      if (tickLabelRotation > 0) {
        x = +qSin(radians) * labelData.totalBounds.height();
        y = flip ? -labelData.totalBounds.width() / 2.0
                 : -qCos(radians) * labelData.totalBounds.height() / 2.0;
      } else {
        x = 0;
        y = flip ? +labelData.totalBounds.width() / 2.0
                 : -qCos(-radians) * labelData.totalBounds.height() / 2.0;
      }
    } else {
      x = 0;
      y = -labelData.totalBounds.height() / 2.0;
    }
  } else if ((type == BaseChartAxis::atTop &&
              tickLabelSide == BaseChartAxis::lsOutside) ||
             (type == BaseChartAxis::atBottom &&
              tickLabelSide == BaseChartAxis::lsInside))  // Anchor at bottom
                                                          // side of tick label
  {
    if (doRotation) {
      if (tickLabelRotation > 0) {
        x = -qCos(radians) * labelData.totalBounds.width() +
            qSin(radians) * labelData.totalBounds.height() / 2.0;
        y = -qSin(radians) * labelData.totalBounds.width() -
            qCos(radians) * labelData.totalBounds.height();
      } else {
        x = -qSin(-radians) * labelData.totalBounds.height() / 2.0;
        y = -qCos(-radians) * labelData.totalBounds.height();
      }
    } else {
      x = -labelData.totalBounds.width() / 2.0;
      y = -labelData.totalBounds.height();
    }
  } else if ((type == BaseChartAxis::atBottom &&
              tickLabelSide == BaseChartAxis::lsOutside) ||
             (type == BaseChartAxis::atTop &&
              tickLabelSide ==
                  BaseChartAxis::lsInside))  // Anchor at top side of tick label
  {
    if (doRotation) {
      if (tickLabelRotation > 0) {
        x = +qSin(radians) * labelData.totalBounds.height() / 2.0;
        y = 0;
      } else {
        x = -qCos(-radians) * labelData.totalBounds.width() -
            qSin(-radians) * labelData.totalBounds.height() / 2.0;
        y = +qSin(-radians) * labelData.totalBounds.width();
      }
    } else {
      x = -labelData.totalBounds.width() / 2.0;
      y = 0;
    }
  }

  return {x, y};
}

/*! \internal

  Simulates the steps done by \ref placeTickLabel by calculating bounding boxes
  of the text label to be drawn, depending on number format etc. Since only the
  largest tick label is wanted for the margin calculation, the passed \a
  tickLabelsSize is only expanded, if it's currently set to a smaller
  width/height.
*/
void BaseChartAxisPainterPrivate::getMaxTickLabelSize(
    const QFont& font, const QString& text, QSize* tickLabelsSize) const {
  // note: this function must return the same tick label sizes as the
  // placeTickLabel function.
  QSize finalSize;
  if (mParentPlot->plottingHints().testFlag(phCacheLabels) &&
      mLabelCache.contains(
          text))  // label caching enabled and have cached label
  {
    const CachedLabel* cachedLabel = mLabelCache.object(text);
    finalSize =
        cachedLabel->pixmap.size() / mParentPlot->bufferDevicePixelRatio();
  } else  // label caching disabled or no label with this text cached:
  {
    TickLabelData labelData = getTickLabelData(font, text);
    finalSize = labelData.rotatedTotalBounds.size();
  }

  // expand passed tickLabelsSize if current tick label is larger:
  if (finalSize.width() > tickLabelsSize->width())
    tickLabelsSize->setWidth(finalSize.width());
  if (finalSize.height() > tickLabelsSize->height())
    tickLabelsSize->setHeight(finalSize.height());
}